Skip to content

Commit

Permalink
copy over the most important files from server
Browse files Browse the repository at this point in the history
  • Loading branch information
Metauriel committed Oct 6, 2023
1 parent 10f554b commit 94535b8
Show file tree
Hide file tree
Showing 14 changed files with 778 additions and 0 deletions.
62 changes: 62 additions & 0 deletions docs/schulcloud-server/Coding-Guidelines/code-style.md
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 docs/schulcloud-server/Coding-Guidelines/exception-handling.md
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);
}
}
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions docs/schulcloud-server/Coding-Guidelines/logging.md
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`.
Loading

0 comments on commit 94535b8

Please sign in to comment.