Skip to content

Commit 2f1692f

Browse files
Refactor code and fix validation errors
1 parent 34f16e0 commit 2f1692f

File tree

8 files changed

+83
-35
lines changed

8 files changed

+83
-35
lines changed

src/_common/errors/BadConfig.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { HttpException } from '@nestjs/common';
2+
3+
export class ValidationException extends HttpException {
4+
constructor(message, statusCode = 400) {
5+
super(message, statusCode);
6+
this.name = this.constructor.name;
7+
this.getStatus = () => statusCode;
8+
}
9+
}
10+
11+
export class ValidationSchemaException extends ValidationException {}
12+
13+
export class ValidationConfigException extends ValidationException {}

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { utilities as nestWinstonModuleUtilities, WinstonModule } from 'nest-win
77
import { createLogger } from 'winston';
88
import * as winston from 'winston';
99
import 'winston-mongodb';
10+
import { appendFile } from 'fs';
1011

1112
declare const module: any;
1213
(async (): Promise<void> => {
@@ -59,6 +60,7 @@ declare const module: any;
5960
// eslint-disable-next-line @typescript-eslint/no-var-requires
6061
(await import('./swagger')).default(app);
6162
}
63+
6264
await app.listen(3000, async (): Promise<void> => {
6365
Logger.log(`Application is running on port: ${process.env.PORT || 3000}`);
6466
});

src/management/identities/identities.controller.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import { Body, Controller, Delete, Get, HttpStatus, Param, Patch, Post, Res } from '@nestjs/common';
1+
import {
2+
BadRequestException,
3+
Body,
4+
Controller,
5+
Delete,
6+
Get,
7+
HttpStatus,
8+
Param,
9+
Patch,
10+
Post,
11+
Res,
12+
} from '@nestjs/common';
213
import { IdentitiesDto, IdentitiesCreateDto, IdentitiesUpdateDto } from './_dto/identities.dto';
314
import { IdentitiesService } from './identities.service';
415
import { AbstractController } from '~/_common/abstracts/abstract.controller';
@@ -18,6 +29,8 @@ import { IdentitiesValidationService } from './validations/identities.validation
1829
import { MixedValue } from '~/_common/types/mixed-value.type';
1930
import { Identities } from './_schemas/identities.schema';
2031
import { Document } from 'mongoose';
32+
import { ValidationConfigException, ValidationSchemaException } from '~/_common/errors/ValidationException';
33+
import { IdentityState } from './_enums/states.enum';
2134

2235
@ApiTags('management')
2336
@Controller('identities')
@@ -52,16 +65,26 @@ export class IdentitiesController extends AbstractController {
5265
>
5366
> {
5467
try {
68+
let statusCode = HttpStatus.CREATED;
69+
let message = null;
5570
const data = await this._service.create<Identities>(body);
56-
return res.status(HttpStatus.CREATED).json({
57-
statusCode: HttpStatus.CREATED,
71+
if (data.state === IdentityState.TO_COMPLETE) {
72+
statusCode = HttpStatus.ACCEPTED;
73+
message = 'Identitée créée avec succès, mais des champs additionnels sont manquants ou invalides.';
74+
}
75+
return res.status(statusCode).json({
76+
statusCode,
5877
data,
78+
message,
5979
});
6080
} catch (error) {
81+
let validations = error.validations;
82+
if (error instanceof ValidationSchemaException || error instanceof ValidationConfigException)
83+
validations = error.getResponse().response;
6184
return res.status(HttpStatus.BAD_REQUEST).json({
6285
statusCode: HttpStatus.BAD_REQUEST,
6386
message: error.message,
64-
validations: error.validations,
87+
validations: validations,
6588
});
6689
}
6790
}
@@ -107,10 +130,12 @@ export class IdentitiesController extends AbstractController {
107130
data,
108131
});
109132
} catch (error) {
133+
let validations = error.validations;
134+
if (error instanceof BadRequestException) validations = error.getResponse();
110135
return res.status(HttpStatus.BAD_REQUEST).json({
111136
statusCode: HttpStatus.BAD_REQUEST,
112137
message: error.message,
113-
validations: error.validations,
138+
validations: validations,
114139
});
115140
}
116141
}

src/management/identities/identities.module.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { IdentitiesController } from './identities.controller';
66
import { IdentitiesValidationService } from './validations/identities.validation.service';
77
import { IdentitiesValidationModule } from './validations/identities.validation.module';
88
import { IdentityState } from './_enums/states.enum';
9+
import { ValidationConfigException, ValidationSchemaException } from '~/_common/errors/ValidationException';
910

1011
@Module({
1112
imports: [
@@ -21,26 +22,39 @@ import { IdentityState } from './_enums/states.enum';
2122
// If the validation fails and the state is TO_CREATE, the state is set to TO_COMPLETE
2223
// Else the error is thrown
2324
schema.pre('validate', async function (next) {
25+
const commonLogMessage = `additionalFields error for ${this.inetOrgPerson.cn} `;
2426
try {
2527
Logger.log(`additionalFields validation start for ${this.inetOrgPerson.cn} `);
28+
2629
await validationService.validate(this.additionalFields);
30+
2731
Logger.log(`additionalFields validation end for ${this.inetOrgPerson.cn} `);
2832
} catch (error) {
29-
Logger.error(`additionalFields validation error for ${this.inetOrgPerson.cn} `, error);
30-
if (error instanceof BadRequestException) {
31-
Logger.error(`additionalFields validation error for ${this.inetOrgPerson.cn} `, error.getResponse());
32-
throw new Error(error.getResponse().toString());
33+
// If the error is a ValidationConfigException, we throw it
34+
if (error instanceof ValidationConfigException) {
35+
Logger.error(commonLogMessage, error.getResponse());
36+
throw new ValidationConfigException(error);
37+
}
38+
39+
// If the error is a ValidationSchemaException and the state is TO_CREATE, we set it to TO_COMPLETE
40+
// Else we throw it
41+
if (error instanceof ValidationSchemaException) {
42+
Logger.error(commonLogMessage, error.getResponse());
43+
if (this.state === IdentityState.TO_CREATE) {
44+
Logger.warn(commonLogMessage, 'Setting state to TO_COMPLETE');
45+
this.state = IdentityState.TO_COMPLETE;
46+
} else throw new ValidationSchemaException(error);
47+
} else {
48+
Logger.error(commonLogMessage, error);
49+
throw new Error(error);
3350
}
34-
if (this.state === IdentityState.TO_CREATE) this.state = IdentityState.TO_COMPLETE;
35-
else throw new Error(error);
3651
}
3752
next();
3853
});
3954

4055
// Pre save hook
4156
// This hook is used to set the state to TO_SYNC if the state is TO_CREATE
4257
schema.pre('save', async function (next) {
43-
console.log('pre save');
4458
if (this.state === IdentityState.TO_CREATE) this.state = IdentityState.TO_VALIDATE;
4559
next();
4660
});

src/management/identities/validations/_config/supann.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ $schema: "http://json-schema.org/draft-07/schema#"
22
type: "object"
33
properties:
44
supannEmpId:
5-
type: "array"
5+
type: "string"
66
description: "Employee ID"
7-
items:
8-
type: "string"
97
supannCivilite:
108
type: "string"
119
description: "Title (Mr, Ms, etc.)"

src/management/identities/validations/_config/validSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const validSchema = {
1919
properties: {
2020
type: {
2121
type: 'string',
22-
const: 'string',
22+
enum: ['string', 'number', 'boolean', 'array', 'object', 'date'],
2323
errorMessage: "Chaque propriété doit avoir un 'type' de 'string'.",
2424
},
2525
description: {

src/management/identities/validations/identities.validation.service.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BadRequestException, Injectable } from '@nestjs/common';
1+
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
22
import { parse } from 'yaml';
33
import { existsSync, readFileSync } from 'fs';
44
import { ConfigObjectSchemaDTO } from './_dto/config.dto';
@@ -9,7 +9,7 @@ import Ajv from 'ajv';
99
import { buildYup } from 'schema-to-yup';
1010
import ajvErrors from 'ajv-errors';
1111
import validSchema from './_config/validSchema';
12-
import { BadConfig } from '~/_common/errors/BadConfig';
12+
import { ValidationConfigException, ValidationSchemaException } from '~/_common/errors/ValidationException';
1313

1414
/**
1515
* Service responsible for validating identities.
@@ -18,10 +18,12 @@ import { BadConfig } from '~/_common/errors/BadConfig';
1818
export class IdentitiesValidationService {
1919
private ajv: Ajv = new Ajv({ allErrors: true });
2020
private validateSchema;
21+
private logger: Logger;
2122

2223
constructor() {
2324
ajvErrors(this.ajv);
2425
this.validateSchema = this.ajv.compile(validSchema);
26+
this.logger = new Logger();
2527
}
2628

2729
/**
@@ -38,21 +40,23 @@ export class IdentitiesValidationService {
3840

3941
// Check for missing attributes
4042
for (const attribute of diff(objectClasses, attributesKeys)) {
41-
validations[attribute] = `Missing attribute '${attribute}'`;
43+
validations[attribute] = `Attribut '${attribute}' manquant dans les champs additionnels`;
4244
reject = true;
4345
}
4446

4547
for (const key of attributesKeys) {
4648
if (!objectClasses.includes(key)) {
4749
validations[key] =
48-
`${key} is not a valid object class in this context, valid object classes are: ${objectClasses.join(', ')}'`;
50+
`${key} n'est pas une classe d'objet valide dans ce contexte, les classes d'objets valides sont: ${objectClasses.join(
51+
', ',
52+
)}'`;
4953
reject = true;
5054
continue;
5155
}
5256

5357
const path = `./src/management/identities/validations/_config/${key}.yml`;
5458
if (!existsSync(path)) {
55-
validations[key] = `Config '${key}.yml' not found`;
59+
validations[key] = `Fichier de config '${key}.yml' introuvable`;
5660
reject = true;
5761
continue;
5862
}
@@ -66,7 +70,7 @@ export class IdentitiesValidationService {
6670
}
6771

6872
if (reject) {
69-
throw new BadRequestException(validations);
73+
throw new ValidationConfigException(validations);
7074
}
7175

7276
// Validate each attribute
@@ -79,10 +83,7 @@ export class IdentitiesValidationService {
7983
}
8084

8185
if (reject) {
82-
return Promise.reject({
83-
message: 'Validation failed',
84-
validations: construct(validations),
85-
});
86+
throw new ValidationSchemaException(validations);
8687
}
8788
return Promise.resolve({ message: 'Validation succeeded' });
8889
}
@@ -102,7 +103,10 @@ export class IdentitiesValidationService {
102103
await yupSchema.validate(attribute, { strict: true, abortEarly: false });
103104
return null;
104105
} catch (error) {
105-
return error.inner.map((err) => `${key}.${err.path}: ${err.errors[0]}`).join(', ');
106+
return error.inner.reduce((acc, err) => {
107+
acc[err.path] = err.message;
108+
return acc;
109+
}, {});
106110
}
107111
}
108112
}

0 commit comments

Comments
 (0)