-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
copy over the most important files from server
- Loading branch information
Showing
14 changed files
with
778 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# Code Style | ||
|
||
## Function | ||
|
||
### Naming | ||
|
||
The name of a function should clearly communicate what it does. There should be no need to read the implementation of a function to understand what it does. | ||
|
||
There are a few keywords that we use with specific meaning: | ||
|
||
#### is... | ||
|
||
`isTask()`, `isPublished()`, `isAuthenticated()`, `isValid()` | ||
|
||
A function with the prefix "is..." is checking wether the input belongs to a certain (sub)class, or fulfils a specific criteria. | ||
|
||
The function should return a boolean, and have no sideeffects. | ||
|
||
#### check... | ||
|
||
`checkPermission()`, `checkInputIsValid()` | ||
|
||
A function with the prefix "check..." is checking the condition described in its name, throwing an error if it does not apply. | ||
|
||
#### has... | ||
|
||
`hasPermission()`, | ||
|
||
similar to "is...", the prefix "has..." means that the function is checking a condition, and returns a boolean. It does NOT throw an error. | ||
|
||
## Classes | ||
|
||
### Order of declarations | ||
|
||
Classes are declared in the following order: | ||
|
||
1. properties | ||
2. constructor | ||
3. methods | ||
|
||
Example: | ||
|
||
```Typescript | ||
export class Course { | ||
// 1. properties | ||
name: string; | ||
|
||
// more properties... | ||
|
||
// 2. constructor | ||
constructor(props: { name: string }) { | ||
// ... | ||
} | ||
|
||
// 3. methods | ||
getShortTitle(): string { | ||
// ... | ||
} | ||
|
||
// more methods... | ||
} | ||
``` |
74 changes: 74 additions & 0 deletions
74
docs/schulcloud-server/Coding-Guidelines/exception-handling.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# Exception Handling | ||
|
||
![](./img/exception-hierarchy.svg) | ||
|
||
We separate our business exceptions from technical exceptions. While for technical exceptions, we use the predefined HTTPExceptions from NestJS, business exceptions inherit from abstract BusinessException. | ||
|
||
By default, implementations of BusinessException must define | ||
|
||
```JSON | ||
code: 500 | ||
type: "CUSTOM_ERROR_TYPE", | ||
title: "Custom Error Type", | ||
message: "Human readable details", | ||
// additional: optionalData | ||
``` | ||
|
||
There is a GlobalErrorFilter provided to handle exceptions, which cares about the response format of exceptions and logging. It overrides the default NestJS APP_FILTER in the core/error-module. | ||
|
||
In client applications, for technical errors, evaluate the http-error-code, then for business exceptions, the type can be used as identifier and additional data can be evaluated. | ||
|
||
For business errors we use 409/conflict as default to clearly have all business errors with one error code identified. | ||
|
||
> Sample: For API validation errors, 400/Bad Request will be extended with `validationError: ValidationError[{ field: string, error: string }]` and a custom type `API_VALIDATION_ERROR`. | ||
Pipes can be used as input validation. To get errors reported in the correct format, they can define a custom exception factory when they should produce api validation error or other exceptions, handled by clients. | ||
|
||
## Chaining errors with the `cause` property | ||
|
||
If you catch an error and throw a new one, put the original error in the [`cause` property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) of the new error. See example: | ||
|
||
```typescript | ||
try { | ||
someMethod(); | ||
} catch(error) { | ||
throw new ForbiddenException('some message', { cause: error }); | ||
} | ||
``` | ||
|
||
|
||
## Loggable exceptions | ||
|
||
If you want the error log to contain more information than just the exception message, use or create an exception which implements the `Loggable` interface. Don't put data directly in the exception message! | ||
|
||
A loggable exception should extend the respective [Built-in HTTP exception](https://docs.nestjs.com/exception-filters#built-in-http-exceptions) from NestJS. For the name just put in "Loggable" before the word "Exception", e.g. "BadRequestLoggableException". Except for logging a loggable exception behaves like any other exception, specifically the error response is not affected by this. | ||
|
||
See example below. | ||
|
||
```TypeScript | ||
export class UnauthorizedLoggableException extends UnauthorizedException implements Loggable { | ||
constructor(private readonly username: string, private readonly systemId?: string) { | ||
super(); | ||
} | ||
|
||
getLogMessage(): ErrorLogMessage { | ||
const message = { | ||
type: 'UNAUTHORIZED_EXCEPTION', | ||
stack: this.stack, | ||
data: { | ||
userName: this.username, | ||
systemId: this.systemId, | ||
}, | ||
}; | ||
|
||
return message; | ||
} | ||
} | ||
``` | ||
```TypeScript | ||
export class YourService { | ||
public sampleServiceMethod(username, systemId) { | ||
throw new UnauthorizedLoggableException(username, systemId); | ||
} | ||
} | ||
``` |
1 change: 1 addition & 0 deletions
1
docs/schulcloud-server/Coding-Guidelines/img/exception-hierarchy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Logging | ||
|
||
For logging use the Logger, exported by the logger module. It encapsulates a [Winston](https://github.com/winstonjs/winston) logger. Its [injection scope](https://docs.nestjs.com/fundamentals/injection-scopes) is transient, so you can set a context when you inject it. | ||
|
||
For better privacy protection and searchability of logs, the logger cannot log arbitrary strings but only so called __loggables__. If you want to log something you have to use or create a loggable that implements the `Loggable` interface. | ||
|
||
The message should be fixed in each loggable. If you want to log further data, put in the data field of the `LogMessage`, like in the example below. | ||
|
||
```TypeScript | ||
export class YourLoggable implements Loggable { | ||
constructor(private readonly userId: EntityId) {} | ||
|
||
getLogMessage(): LogMessage { | ||
return { | ||
message: 'I am a log message.', | ||
data: { userId: this.userId, }, | ||
}; | ||
} | ||
} | ||
|
||
``` | ||
|
||
```TypeScript | ||
import { Logger } from '@src/core/logger'; | ||
|
||
export class YourUc { | ||
constructor(private logger: Logger) { | ||
this.logger.setContext(YourUc.name); | ||
} | ||
|
||
public sampleUcMethod(user) { | ||
this.logger.log(new YourLoggable(userId: user.id)); | ||
} | ||
} | ||
``` | ||
|
||
This produces a logging output like | ||
|
||
``` | ||
[NestWinston] Info - 2023-05-31 15:20:30.888 [YourUc] { message: 'I am a log message.', data: { userId: '0000d231816abba584714c9e' }} | ||
``` | ||
|
||
## Log levels and error logging | ||
|
||
The logger exposes the methods `log`, `warn`, `debug` and `verbose`. It does not expose an `error` method because we don't want errors to be logged manually. All errors are logged in the exception filter. | ||
|
||
## Legacy logger | ||
|
||
While transitioning to the new logger for loggables, the old logger for strings is still available as `LegacyLogger`. |
Oops, something went wrong.