Skip to content

Commit 6a67891

Browse files
committed
feat: implement generator module
1 parent 6f84bf0 commit 6a67891

32 files changed

+885
-65
lines changed

api/eslint.config.mjs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ export default tseslint.config(
2929
import: eslintImport,
3030
},
3131
rules: {
32-
'@typescript-eslint/consistent-type-exports': 'error',
33-
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
34-
'@typescript-eslint/explicit-function-return-type': ['warn', { allowExpressions: true }],
35-
'@typescript-eslint/explicit-member-accessibility': ['warn', { accessibility: 'explicit' }],
3632
'@typescript-eslint/member-ordering': ['warn', {
3733
default: [
3834
'public-static-field',
@@ -47,16 +43,21 @@ export default tseslint.config(
4743
'private-instance-method',
4844
],
4945
},],
46+
'@typescript-eslint/consistent-type-exports': 'error',
47+
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
48+
'@typescript-eslint/explicit-function-return-type': ['warn', { allowExpressions: true }],
49+
'@typescript-eslint/explicit-member-accessibility': 'off',
5050
'@typescript-eslint/no-explicit-any': 'off',
5151
'@typescript-eslint/no-floating-promises': 'error',
5252
'@typescript-eslint/no-misused-promises': 'warn',
5353
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
5454
'@typescript-eslint/no-unsafe-argument': 'warn',
5555
'@typescript-eslint/no-unsafe-assignment': 'off',
5656
'@typescript-eslint/no-unsafe-call': 'off',
57+
'@typescript-eslint/no-unsafe-member-access': 'off',
5758
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
5859
'@typescript-eslint/promise-function-async': 'warn',
59-
'@typescript-eslint/strict-boolean-expressions': 'warn',
60+
'@typescript-eslint/strict-boolean-expressions': 'off',
6061
'@typescript-eslint/unbound-method': 'off',
6162
'curly': 'error',
6263
'no-console': 'warn',

api/package.json

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,6 @@
4949
"typescript-eslint": "8.41.0"
5050
},
5151
"jest": {
52-
"moduleFileExtensions": [
53-
"js",
54-
"json",
55-
"ts"
56-
],
57-
"rootDir": "./",
58-
"testRegex": ".*\\.spec\\.ts$",
59-
"transform": {
60-
"^.+\\.(t|j)s$": "ts-jest"
61-
},
6252
"collectCoverageFrom": [
6353
"src/**/*.{ts,js}",
6454
"!src/**/*.dto.ts",
@@ -71,6 +61,17 @@
7161
"!dist/**"
7262
],
7363
"coverageDirectory": "coverage",
74-
"testEnvironment": "node"
64+
"extensionsToTreatAsEsm": [".ts"],
65+
"moduleFileExtensions": ["js", "json", "ts"],
66+
"rootDir": "./",
67+
"testEnvironment": "node",
68+
"testRegex": ".*\\.spec\\.ts$",
69+
"transformIgnorePatterns": ["node_modules/(?!(@faker-js/faker)/)"],
70+
"transform": {
71+
"^.+\\.(t|j)s$": "ts-jest",
72+
"^.+\\.tsx?$": ["ts-jest", {
73+
"useESM": true
74+
}]
75+
}
7576
}
7677
}

api/src/app.controller.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { AppService } from './app.service';
33

44
@Controller()
55
export class AppController {
6-
public constructor(private appService: AppService) {}
6+
constructor(private appService: AppService) {}
77

88
@Get()
9-
public getHello(): string {
9+
getHello(): string {
1010
return this.appService.getHello();
1111
}
1212
}

api/src/app.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { FakerModule } from './modules/faker';
1818
],
1919
})
2020
export class AppModule implements NestModule {
21-
public configure(consumer: MiddlewareConsumer): void {
21+
configure(consumer: MiddlewareConsumer): void {
2222
consumer.apply(HeadersMiddleware).forRoutes('*');
2323
consumer.apply(CacheMiddleware).forRoutes('*');
2424
consumer.apply(MetadataMiddleware).forRoutes('*');

api/src/app.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
22

33
@Injectable()
44
export class AppService {
5-
public getHello(): string {
5+
getHello(): string {
66
return 'Hello World!';
77
}
88
}
Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,50 @@
11
import type { ValidationError } from '@nestjs/common';
22
import { BadRequestException } from '@nestjs/common';
33

4-
export function validationExceptionFactory(errors: ValidationError[]): BadRequestException {
5-
const messages = errors.reduce((previousValue, error) => {
6-
if (error.constraints) {
7-
previousValue[error.property] = [...Object.values(error.constraints)];
8-
}
4+
interface FieldErrors {
5+
[fieldIndex: string]: { [fieldProperty: string]: string[]; };
6+
}
7+
8+
interface StructuredErrors {
9+
[key: string]: string[] | FieldErrors;
10+
}
911

10-
return previousValue;
11-
}, {} as Record<string, string[]>);
12+
export function validationExceptionFactory(errors: ValidationError[]): BadRequestException {
13+
const structuredErrors = createStructureErrors(errors);
1214

1315
return new BadRequestException({
1416
message: 'Request validation failed. Please check the provided data',
15-
errors: messages,
17+
errors: structuredErrors,
1618
type: 'VALIDATION_ERROR',
1719
timestamp: new Date().toISOString(),
1820
});
19-
}
21+
}
22+
23+
function createStructureErrors(errors: ValidationError[]): StructuredErrors {
24+
const result: StructuredErrors = {};
25+
26+
errors.forEach(error => {
27+
if (error.children && Array.isArray(error.children) && error.children.length > 0) {
28+
const fieldsErrors: FieldErrors = {};
29+
30+
error.children.forEach((fieldError) => {
31+
if (fieldError.children && Array.isArray(fieldError.children) && fieldError.children.length > 0) {
32+
const fieldIndex = fieldError.property;
33+
34+
fieldsErrors[fieldIndex] = {};
35+
fieldError.children.forEach(propError => {
36+
if (propError.constraints) {
37+
fieldsErrors[fieldIndex][propError.property] = Object.values(propError.constraints);
38+
}
39+
});
40+
}
41+
});
42+
43+
result.fields = fieldsErrors;
44+
} else if (error.constraints) {
45+
result[error.property] = Object.values(error.constraints);
46+
}
47+
});
48+
49+
return result;
50+
}

api/src/middlewares/cache/cache.middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const MAX_AGE = 60 * 60 * 24 * 30;
77

88
@Injectable()
99
export class CacheMiddleware implements NestMiddleware {
10-
public use(req: Request, res: Response, next: NextFunction): void {
10+
use(req: Request, res: Response, next: NextFunction): void {
1111
const isStaticFile = REG_EXP.test(req.originalUrl);
1212
const cacheControl = isStaticFile ? `public, max-age=${MAX_AGE}` : 'no-store';
1313

api/src/middlewares/headers/headers.middleware.spec.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ describe('HeadersMiddleware', () => {
3737
expect(res.setHeader).toHaveBeenCalledWith('Cross-Origin-Resource-Policy', 'same-origin');
3838
});
3939

40-
it('should set Expect-CT header', () => {
41-
middleware.use(req as Request, res as Response, next);
42-
expect(res.setHeader).toHaveBeenCalledWith('Expect-CT', 'max-age=86400, enforce');
43-
});
44-
4540
it('should set Permissions-Policy header', () => {
4641
middleware.use(req as Request, res as Response, next);
4742
expect(res.setHeader).toHaveBeenCalledWith('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
@@ -57,11 +52,6 @@ describe('HeadersMiddleware', () => {
5752
expect(res.setHeader).toHaveBeenCalledWith('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
5853
});
5954

60-
it('should set Vary header', () => {
61-
middleware.use(req as Request, res as Response, next);
62-
expect(res.setHeader).toHaveBeenCalledWith('Vary', 'Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site');
63-
});
64-
6555
it('should set X-Content-Type-Options header', () => {
6656
middleware.use(req as Request, res as Response, next);
6757
expect(res.setHeader).toHaveBeenCalledWith('X-Content-Type-Options', 'nosniff');

api/src/middlewares/headers/headers.middleware.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ import { Request, Response, NextFunction } from 'express';
33

44
@Injectable()
55
export class HeadersMiddleware implements NestMiddleware {
6-
public use(_req: Request, res: Response, next: NextFunction): void {
6+
use(_req: Request, res: Response, next: NextFunction): void {
77
res.setHeader('Content-Security-Policy', "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';");
88
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
99
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
1010
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
11-
res.setHeader('Expect-CT', 'max-age=86400, enforce');
1211
res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
1312
res.setHeader('Referrer-Policy', 'no-referrer');
1413
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
15-
res.setHeader('Vary', 'Sec-Fetch-Dest, Sec-Fetch-Mode, Sec-Fetch-Site');
1614
res.setHeader('X-Content-Type-Options', 'nosniff');
1715
res.setHeader('X-Frame-Options', 'SAMEORIGIN');
1816
res.setHeader('X-XSS-Protection', '1; mode=block');

api/src/middlewares/metadata/metadata.middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Request, Response, NextFunction } from 'express';
33

44
@Injectable()
55
export class MetadataMiddleware implements NestMiddleware {
6-
public use(req: Request, res: Response, next: NextFunction): void {
6+
use(req: Request, res: Response, next: NextFunction): void {
77
if (req.originalUrl === '/') {
88
res.setHeader('X-App-CreatedBy', 'gappbox');
99
res.setHeader('X-App-Name', 'mockly');

0 commit comments

Comments
 (0)