nestcraftx 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bin/nestcraft.js +24 -0
- package/commands/start.js +8 -0
- package/nestcraft-0.1.0.tgz +0 -0
- package/package.json +31 -0
- package/readme +181 -0
- package/setup.js +33 -0
- package/unutils.txt +173 -0
- package/utils/configs/configureDocker.js +16 -0
- package/utils/configs/setupCleanArchitecture.js +579 -0
- package/utils/loggers/logError.js +5 -0
- package/utils/loggers/logInfo.js +4 -0
- package/utils/loggers/logSuccess.js +4 -0
- package/utils/setups/orms/typeOrmSetup.js +120 -0
- package/utils/setups/projectSetup.js +29 -0
- package/utils/setups/setupAuth.js +452 -0
- package/utils/setups/setupDatabase.js +73 -0
- package/utils/setups/setupPrisma.js +226 -0
- package/utils/setups/setupSwagger.js +87 -0
- package/utils/shell.js +19 -0
- package/utils/userInput.js +517 -0
- package/utils/utils.js +723 -0
package/utils/utils.js
ADDED
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
import { logInfo } from "./loggers/logInfo.js";
|
|
2
|
+
import { logSuccess } from "./loggers/logSuccess.js";
|
|
3
|
+
import { createDirectory, createFile, updateFile } from "./userInput.js";
|
|
4
|
+
|
|
5
|
+
export async function generateEntityFileContent(entity) {
|
|
6
|
+
// console.log("Entity name:", entity.name); // Log de l'entité
|
|
7
|
+
|
|
8
|
+
if (!entity || !entity.name) {
|
|
9
|
+
throw new Error("Nom de l'entité manquant !");
|
|
10
|
+
}
|
|
11
|
+
const entityName = capitalize(entity.name);
|
|
12
|
+
const className = `${entityName}Entity`;
|
|
13
|
+
|
|
14
|
+
const defaultFields = [
|
|
15
|
+
{
|
|
16
|
+
name: "id",
|
|
17
|
+
type: "string",
|
|
18
|
+
comment:
|
|
19
|
+
"L'identifiant unique de l'entité.\n * Utilisé pour retrouver de manière unique un enregistrement dans la base de données.\n *\n * Exemple : '123e4567-e89b-12d3-a456-426614174000'",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: "createdAt",
|
|
23
|
+
type: "Date",
|
|
24
|
+
comment:
|
|
25
|
+
"La date de création de l'entité.\n * Définie lors de la création et ne change pas.\n *\n * Exemple : new Date('2022-01-01T10:00:00Z')",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "updatedAt",
|
|
29
|
+
type: "Date",
|
|
30
|
+
comment:
|
|
31
|
+
"La date de dernière mise à jour de l'entité.\n * Mise à jour à chaque modification.\n *\n * Exemple : new Date('2022-02-01T15:00:00Z')",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const isUserEntityWithRole =
|
|
36
|
+
entity.name.toLowerCase() === "user" &&
|
|
37
|
+
entity.fields.some((f) => f.name === "role");
|
|
38
|
+
|
|
39
|
+
const allFields = [...defaultFields, ...entity.fields];
|
|
40
|
+
|
|
41
|
+
const constructorParams = allFields
|
|
42
|
+
.map(
|
|
43
|
+
(f) => `
|
|
44
|
+
/**
|
|
45
|
+
* ${f.comment || `Champ ${f.name}`}
|
|
46
|
+
*/
|
|
47
|
+
private readonly ${f.name}: ${getFormattedType(f)}
|
|
48
|
+
,`
|
|
49
|
+
)
|
|
50
|
+
.join("");
|
|
51
|
+
|
|
52
|
+
const constructorAssignments = allFields
|
|
53
|
+
.map((f) => ` this.${f.name} = ${f.name};`)
|
|
54
|
+
.join("\n");
|
|
55
|
+
|
|
56
|
+
const getters = allFields
|
|
57
|
+
.map(
|
|
58
|
+
(f) => `
|
|
59
|
+
get${capitalize(f.name)}(): ${getFormattedType(f)}
|
|
60
|
+
{
|
|
61
|
+
return this.${f.name};
|
|
62
|
+
}`
|
|
63
|
+
)
|
|
64
|
+
.join("\n");
|
|
65
|
+
|
|
66
|
+
const jsonFields = allFields
|
|
67
|
+
.map((f) => ` ${f.name}: this.${f.name},`)
|
|
68
|
+
.join("\n");
|
|
69
|
+
|
|
70
|
+
let importStatements = "";
|
|
71
|
+
if (isUserEntityWithRole) {
|
|
72
|
+
importStatements += `import { Role } from 'src/modules/user/domain/enums/role.enum';\n\n`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return `${importStatements}/**
|
|
76
|
+
* ${className} représente l'entité principale de ${entityName} dans le domaine.
|
|
77
|
+
* Elle contient les propriétés de base nécessaires à la gestion des données liées à ${entityName}.
|
|
78
|
+
*/
|
|
79
|
+
export class ${className} {
|
|
80
|
+
constructor(${constructorParams}
|
|
81
|
+
) {
|
|
82
|
+
${constructorAssignments}
|
|
83
|
+
}
|
|
84
|
+
${getters}
|
|
85
|
+
|
|
86
|
+
toJSON() {
|
|
87
|
+
return {
|
|
88
|
+
${jsonFields}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function generateMapper(entity) {
|
|
96
|
+
const entityName = capitalize(entity.name);
|
|
97
|
+
|
|
98
|
+
// verification de l'existance de user entity
|
|
99
|
+
const isUserWithRole =
|
|
100
|
+
entity.name.toLowerCase() === "user" &&
|
|
101
|
+
entity.fields.some((f) => f.name.toLowerCase() === "role");
|
|
102
|
+
|
|
103
|
+
const domainArgs = ["data.id"]
|
|
104
|
+
.concat(["data.createdAt", "data.updatedAt"])
|
|
105
|
+
.concat(entity.fields.map((f) => `data.${f.name}`))
|
|
106
|
+
.join(",\n ");
|
|
107
|
+
|
|
108
|
+
const toPersistenceFields = entity.fields
|
|
109
|
+
.map((f) => `${f.name}: dto.${f.name},`)
|
|
110
|
+
.join("\n ");
|
|
111
|
+
|
|
112
|
+
const toUpdateFields = entity.fields
|
|
113
|
+
.map(
|
|
114
|
+
(f) => `if (dto.${f.name} !== undefined) data.${f.name} = dto.${f.name};`
|
|
115
|
+
)
|
|
116
|
+
.join("\n ");
|
|
117
|
+
|
|
118
|
+
return `
|
|
119
|
+
import { Injectable } from '@nestjs/common';
|
|
120
|
+
import { ${entityName}Entity } from 'src/${decapitalize(
|
|
121
|
+
entity.name
|
|
122
|
+
)}/domain/entities/${decapitalize(entity.name)}.entity';
|
|
123
|
+
import { Create${entityName}Dto, Update${entityName}Dto } from 'src/${
|
|
124
|
+
entity.name
|
|
125
|
+
}/application/dtos/${decapitalize(entity.name)}.dto';
|
|
126
|
+
|
|
127
|
+
${
|
|
128
|
+
isUserWithRole
|
|
129
|
+
? "import { Role } from 'src/modules/user/domain/enums/role.enum';"
|
|
130
|
+
: ""
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@Injectable()
|
|
135
|
+
export class ${entityName}Mapper {
|
|
136
|
+
toDomain(data: any): ${entityName}Entity {
|
|
137
|
+
return new ${entityName}Entity(
|
|
138
|
+
${domainArgs}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
toPersistence(dto: Create${entityName}Dto): any {
|
|
143
|
+
return {
|
|
144
|
+
${toPersistenceFields}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
toUpdatePersistence(dto: Update${entityName}Dto): any {
|
|
149
|
+
const data: any = {};
|
|
150
|
+
${toUpdateFields}
|
|
151
|
+
return data;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function generateDto(entity) {
|
|
158
|
+
const entityName = capitalize(entity.name);
|
|
159
|
+
|
|
160
|
+
const getExampleForField = (f) => {
|
|
161
|
+
// Dynamique et plus réaliste, basé sur le type de champ et des exemples courants
|
|
162
|
+
switch (f.type) {
|
|
163
|
+
case "string":
|
|
164
|
+
return `"${f.name}_example"`; // Exemple générique basé sur le nom du champ
|
|
165
|
+
case "number":
|
|
166
|
+
return f.name === "price" ? 199.99 : 123; // Exemple spécifique pour 'price'
|
|
167
|
+
case "boolean":
|
|
168
|
+
return true; // Exemple générique pour les booléens
|
|
169
|
+
case "Date":
|
|
170
|
+
case "date":
|
|
171
|
+
return `"2024-01-01T00:00:00Z"`; // Exemple générique pour les dates
|
|
172
|
+
case "role":
|
|
173
|
+
return `"user"`; // Exemple pour les rôles, par défaut 'user'
|
|
174
|
+
case "discount":
|
|
175
|
+
return 10; // Exemple pour une valeur de type discount
|
|
176
|
+
case "account":
|
|
177
|
+
return `"acc_${f.name}_12345"`; // Exemple pour un compte
|
|
178
|
+
case "amount":
|
|
179
|
+
return 500.75; // Exemple pour un montant
|
|
180
|
+
default:
|
|
181
|
+
return `"sample_${f.name}"`; // Fallback générique pour les autres types
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const generateFieldLine = (f) => {
|
|
186
|
+
const typeDecorator =
|
|
187
|
+
{
|
|
188
|
+
string: "IsString",
|
|
189
|
+
number: "IsInt",
|
|
190
|
+
boolean: "IsBoolean",
|
|
191
|
+
date: "IsDate",
|
|
192
|
+
}[f.type] || "IsString";
|
|
193
|
+
|
|
194
|
+
const apiPropertyDecorator = f.optional
|
|
195
|
+
? `@ApiPropertyOptional({ example: ${getExampleForField(f)}, type: '${
|
|
196
|
+
f.type
|
|
197
|
+
}' })`
|
|
198
|
+
: `@ApiProperty({ example: ${getExampleForField(f)}, type: '${
|
|
199
|
+
f.type
|
|
200
|
+
}' })`;
|
|
201
|
+
|
|
202
|
+
return `${apiPropertyDecorator}
|
|
203
|
+
@${typeDecorator}()
|
|
204
|
+
${f.name}${f.optional ? "?" : ""}: ${f.type};`;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const dtoFields = entity.fields
|
|
208
|
+
.map((f) => generateFieldLine({ ...f, optional: false }))
|
|
209
|
+
.join("\n\n");
|
|
210
|
+
|
|
211
|
+
const updateDtoFields = entity.fields
|
|
212
|
+
.map((f) => generateFieldLine({ ...f, optional: true }))
|
|
213
|
+
.join("\n\n");
|
|
214
|
+
|
|
215
|
+
return `
|
|
216
|
+
import { IsOptional, IsString, IsInt, IsBoolean, IsDate } from 'class-validator';
|
|
217
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
218
|
+
|
|
219
|
+
// Vous pouvez décommenter ce champ si vous voulez gérer le rôle de l'utilisateur.
|
|
220
|
+
// import { Role } from 'src/modules/user/domain/enums/role.enum';
|
|
221
|
+
|
|
222
|
+
export class Create${entityName}Dto {
|
|
223
|
+
${dtoFields}
|
|
224
|
+
|
|
225
|
+
// Décommentez et ajustez le rôle si nécessaire.
|
|
226
|
+
// @ApiProperty({ example: 'admin', type: 'string' })
|
|
227
|
+
// @IsString()
|
|
228
|
+
// role: Role;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export class Update${entityName}Dto {
|
|
232
|
+
${updateDtoFields}
|
|
233
|
+
|
|
234
|
+
// Décommentez et ajustez le rôle si nécessaire.
|
|
235
|
+
// @ApiPropertyOptional({ example: 'admin', type: 'string' })
|
|
236
|
+
// @IsString()
|
|
237
|
+
// role?: Role;
|
|
238
|
+
}
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export async function generateMiddlewares() {
|
|
243
|
+
logInfo(
|
|
244
|
+
"\u2728 G\u00e9n\u00e9ration des middlewares, interceptors, guards et filters personnalis\u00e9s..."
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const basePath = "src/common";
|
|
248
|
+
await createDirectory(`${basePath}/middlewares`);
|
|
249
|
+
await createDirectory(`${basePath}/interceptors`);
|
|
250
|
+
await createDirectory(`${basePath}/filters`);
|
|
251
|
+
await createDirectory(`${basePath}/decorators`);
|
|
252
|
+
|
|
253
|
+
// Logger Middleware
|
|
254
|
+
await createFile({
|
|
255
|
+
path: `${basePath}/middlewares/logger.middleware.ts`,
|
|
256
|
+
contente: `import { Request, Response, NextFunction } from 'express';
|
|
257
|
+
|
|
258
|
+
export function LoggerMiddleware(
|
|
259
|
+
req: Request,
|
|
260
|
+
res: Response,
|
|
261
|
+
next: NextFunction,
|
|
262
|
+
) {
|
|
263
|
+
console.log(\`[Request] \${req.method} \${req.originalUrl} - \${res.statusCode}\`);
|
|
264
|
+
next();
|
|
265
|
+
}
|
|
266
|
+
`,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Error Handling Filter
|
|
270
|
+
await createFile({
|
|
271
|
+
path: `${basePath}/filters/all-exceptions.filter.ts`,
|
|
272
|
+
contente: `import {
|
|
273
|
+
ExceptionFilter,
|
|
274
|
+
Catch,
|
|
275
|
+
ArgumentsHost,
|
|
276
|
+
HttpException,
|
|
277
|
+
HttpStatus,
|
|
278
|
+
} from '@nestjs/common';
|
|
279
|
+
import { Request, Response } from 'express';
|
|
280
|
+
|
|
281
|
+
@Catch()
|
|
282
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
283
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
284
|
+
const ctx = host.switchToHttp();
|
|
285
|
+
const response = ctx.getResponse<Response>();
|
|
286
|
+
const request = ctx.getRequest<Request>();
|
|
287
|
+
const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
|
|
288
|
+
|
|
289
|
+
const message = exception instanceof HttpException ? exception.getResponse() : 'Internal server error';
|
|
290
|
+
console.log('exception: ', exception);
|
|
291
|
+
|
|
292
|
+
response.status(status).json({
|
|
293
|
+
statusCode: status,
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
path: request.url,
|
|
296
|
+
message: message,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}`,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Response Interceptor
|
|
303
|
+
await createFile({
|
|
304
|
+
path: `${basePath}/interceptors/response.interceptor.ts`,
|
|
305
|
+
contente: `import {
|
|
306
|
+
CallHandler,
|
|
307
|
+
ExecutionContext,
|
|
308
|
+
Injectable,
|
|
309
|
+
NestInterceptor,
|
|
310
|
+
} from '@nestjs/common';
|
|
311
|
+
import { Observable } from 'rxjs';
|
|
312
|
+
import { map } from 'rxjs/operators';
|
|
313
|
+
|
|
314
|
+
@Injectable()
|
|
315
|
+
export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
|
|
316
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
317
|
+
const httpContext = context.switchToHttp();
|
|
318
|
+
const response = httpContext.getResponse();
|
|
319
|
+
const request = httpContext.getRequest();
|
|
320
|
+
|
|
321
|
+
return next.handle().pipe(
|
|
322
|
+
map((data) => {
|
|
323
|
+
const message =
|
|
324
|
+
response.locals.message || this.getDefaultMessage(request.method);
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
statusCode: response.statusCode,
|
|
328
|
+
message,
|
|
329
|
+
path: request.url,
|
|
330
|
+
method: request.method,
|
|
331
|
+
timestamp: new Date().toISOString(),
|
|
332
|
+
data: data ?? null,
|
|
333
|
+
};
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private getDefaultMessage(method: string): string {
|
|
339
|
+
switch (method.toUpperCase()) {
|
|
340
|
+
case 'POST':
|
|
341
|
+
return 'Création réussie';
|
|
342
|
+
case 'PUT':
|
|
343
|
+
case 'PATCH':
|
|
344
|
+
return 'Mise à jour réussie';
|
|
345
|
+
case 'DELETE':
|
|
346
|
+
return 'Suppression réussie';
|
|
347
|
+
default:
|
|
348
|
+
return 'Requête traitée avec succès';
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
`,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// 📌 public Decorator
|
|
356
|
+
await createFile({
|
|
357
|
+
path: `${basePath}/decorators/public.decorator.ts`,
|
|
358
|
+
contente: `import { SetMetadata } from '@nestjs/common';
|
|
359
|
+
|
|
360
|
+
export const IS_PUBLIC_KEY = 'isPublic';
|
|
361
|
+
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
|
|
362
|
+
`,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// 📌 Auth role Decorator
|
|
366
|
+
await createFile({
|
|
367
|
+
path: `${basePath}/decorators/role.decorator.ts`,
|
|
368
|
+
contente: `import { SetMetadata } from '@nestjs/common';
|
|
369
|
+
|
|
370
|
+
export const ROLES_KEY = 'roles';
|
|
371
|
+
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
|
|
372
|
+
`,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Modification de main.ts pour intégrer les middlewares
|
|
376
|
+
// Chemin vers main.ts
|
|
377
|
+
const mainTsPath = "src/main.ts";
|
|
378
|
+
|
|
379
|
+
// ✅ 1. Mise à jour des imports
|
|
380
|
+
const importPattern = "import { AppModule } from './app.module';";
|
|
381
|
+
const importReplacer = `import { AppModule } from 'src/app.module';
|
|
382
|
+
import { AllExceptionsFilter } from 'src/common/filters/all-exceptions.filter';
|
|
383
|
+
import { ResponseInterceptor } from 'src/common/interceptors/response.interceptor';
|
|
384
|
+
import { LoggerMiddleware } from 'src/common/middlewares/logger.middleware';
|
|
385
|
+
import { ValidationPipe } from '@nestjs/common';`;
|
|
386
|
+
|
|
387
|
+
// ✅ 2. Injection dans le contenu de bootstrap()
|
|
388
|
+
const contentPattern = `const app = await NestFactory.create(AppModule);`;
|
|
389
|
+
const contentReplacer = `const app = await NestFactory.create(AppModule);
|
|
390
|
+
|
|
391
|
+
// 🔒 Global filter pour gérer toutes les exceptions
|
|
392
|
+
app.useGlobalFilters(new AllExceptionsFilter());
|
|
393
|
+
|
|
394
|
+
// 🔁 Global interceptor pour structurer les réponses
|
|
395
|
+
// app.useGlobalInterceptors(new ResponseInterceptor()); //deja appliquer dans le app.module.ts par convention (ne choisir que l'un des deux)
|
|
396
|
+
|
|
397
|
+
// 📋 Middleware pour logger toutes les requêtes entrantes
|
|
398
|
+
app.use(LoggerMiddleware);`;
|
|
399
|
+
|
|
400
|
+
// ✅ Appels
|
|
401
|
+
await updateFile({
|
|
402
|
+
path: mainTsPath,
|
|
403
|
+
pattern: importPattern,
|
|
404
|
+
replacement: importReplacer,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
await updateFile({
|
|
408
|
+
path: mainTsPath,
|
|
409
|
+
pattern: contentPattern,
|
|
410
|
+
replacement: contentReplacer,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// modification de AppModule
|
|
414
|
+
const appModulePath = "src/app.module.ts";
|
|
415
|
+
|
|
416
|
+
const addNestModuleInterface = `providers: [`;
|
|
417
|
+
const replaceWithNestModule = `providers: [
|
|
418
|
+
{
|
|
419
|
+
provide: APP_INTERCEPTOR,
|
|
420
|
+
useClass: ResponseInterceptor, // 🔁 Global interceptor pour structurer les réponses
|
|
421
|
+
},`;
|
|
422
|
+
|
|
423
|
+
await updateFile({
|
|
424
|
+
path: appModulePath,
|
|
425
|
+
pattern: addNestModuleInterface,
|
|
426
|
+
replacement: replaceWithNestModule,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
logSuccess(
|
|
430
|
+
"\u2705 Middlewares, filters, interceptors et guards g\u00e9n\u00e9r\u00e9s avec succ\u00e8s !"
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export async function generateRepository(entityName, orm) {
|
|
435
|
+
const entityNameCapitalized = capitalize(entityName);
|
|
436
|
+
const entityNameLower = entityName.toLowerCase();
|
|
437
|
+
const entityPath = `src/${entityNameLower}`;
|
|
438
|
+
|
|
439
|
+
console.log(`entity name: ${entityNameLower} \n omr selected: ${orm}`);
|
|
440
|
+
|
|
441
|
+
// Gère le switch en fonction de l'ORM choisi
|
|
442
|
+
switch (orm) {
|
|
443
|
+
case "typeorm":
|
|
444
|
+
// Implémentation du repository pour TypeORM
|
|
445
|
+
await createFile({
|
|
446
|
+
path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
|
|
447
|
+
contente: `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
448
|
+
import { Repository } from 'typeorm';
|
|
449
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
450
|
+
import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
|
|
451
|
+
import { ${entityNameCapitalized} } from 'src/entities/${entityNameCapitalized}.entity';
|
|
452
|
+
import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
|
|
453
|
+
import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
|
|
454
|
+
import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
|
|
455
|
+
|
|
456
|
+
@Injectable()
|
|
457
|
+
export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
|
|
458
|
+
constructor(
|
|
459
|
+
@InjectRepository(${entityNameCapitalized})
|
|
460
|
+
private readonly repository: Repository<${entityNameCapitalized}>,
|
|
461
|
+
private readonly mapper: ${entityNameCapitalized}Mapper,
|
|
462
|
+
) {}
|
|
463
|
+
|
|
464
|
+
// create
|
|
465
|
+
async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
466
|
+
const toPersist = this.mapper.toPersistence(data);
|
|
467
|
+
const created = await this.repository.save(toPersist);
|
|
468
|
+
return this.mapper.toDomain(created);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// find by id
|
|
472
|
+
async findById(id: string): Promise<${entityNameCapitalized}Entity> {
|
|
473
|
+
const record = await this.repository.findOne({
|
|
474
|
+
where: { id },
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
if (!record) {
|
|
478
|
+
throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return this.mapper.toDomain(record);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// update
|
|
485
|
+
async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
486
|
+
const toUpdate = this.mapper.toUpdatePersistence(data);
|
|
487
|
+
const updated = await this.repository.save({ ...toUpdate, id });
|
|
488
|
+
return this.mapper.toDomain(updated);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// find all
|
|
492
|
+
async findAll(): Promise<${entityNameCapitalized}Entity[]> {
|
|
493
|
+
const records = await this.repository.find();
|
|
494
|
+
return records.map(record => this.mapper.toDomain(record));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// delete
|
|
498
|
+
async delete(id: string): Promise<void> {
|
|
499
|
+
await this.repository.delete(id);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
`,
|
|
503
|
+
});
|
|
504
|
+
break;
|
|
505
|
+
|
|
506
|
+
case "prisma":
|
|
507
|
+
// Implémentation pour Prisma
|
|
508
|
+
await createFile({
|
|
509
|
+
path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
|
|
510
|
+
contente: `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
511
|
+
import { PrismaService } from 'src/prisma/prisma.service';
|
|
512
|
+
import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
|
|
513
|
+
import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
|
|
514
|
+
import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
|
|
515
|
+
import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
|
|
516
|
+
|
|
517
|
+
@Injectable()
|
|
518
|
+
export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
|
|
519
|
+
constructor(
|
|
520
|
+
private readonly prisma: PrismaService,
|
|
521
|
+
private readonly mapper: ${entityNameCapitalized}Mapper,
|
|
522
|
+
) {}
|
|
523
|
+
|
|
524
|
+
// create
|
|
525
|
+
async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
526
|
+
const toPersist = this.mapper.toPersistence(data);
|
|
527
|
+
const created = await this.prisma.${entityNameLower}.create({ data: toPersist });
|
|
528
|
+
return this.mapper.toDomain(created);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// find by id
|
|
532
|
+
async findById(id: string): Promise<${entityNameCapitalized}Entity> {
|
|
533
|
+
const record = await this.prisma.${entityNameLower}.findUnique({
|
|
534
|
+
where: { id },
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
if (!record) {
|
|
538
|
+
throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return this.mapper.toDomain(record);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// update
|
|
545
|
+
async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
546
|
+
const toUpdate = this.mapper.toUpdatePersistence(data);
|
|
547
|
+
const updated = await this.prisma.${entityNameLower}.update({
|
|
548
|
+
where: { id },
|
|
549
|
+
data: toUpdate,
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
return this.mapper.toDomain(updated);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// find all
|
|
556
|
+
async findAll(): Promise<${entityNameCapitalized}Entity[]> {
|
|
557
|
+
const records = await this.prisma.${entityNameLower}.findMany();
|
|
558
|
+
return records.map(record => this.mapper.toDomain(record));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// delete
|
|
562
|
+
async delete(id: string): Promise<void> {
|
|
563
|
+
await this.prisma.${entityNameLower}.delete({
|
|
564
|
+
where: { id },
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
`,
|
|
569
|
+
});
|
|
570
|
+
break;
|
|
571
|
+
|
|
572
|
+
case "mongoose":
|
|
573
|
+
// Implémentation pour MongoDB avec Mongoose
|
|
574
|
+
await createFile({
|
|
575
|
+
path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
|
|
576
|
+
contente: `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
577
|
+
import { InjectModel } from '@nestjs/mongoose';
|
|
578
|
+
import { Model } from 'mongoose';
|
|
579
|
+
import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
|
|
580
|
+
import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
|
|
581
|
+
import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
|
|
582
|
+
import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
|
|
583
|
+
|
|
584
|
+
@Injectable()
|
|
585
|
+
export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
|
|
586
|
+
constructor(
|
|
587
|
+
@InjectModel(${entityNameCapitalized}Entity.name)
|
|
588
|
+
private readonly model: Model<${entityNameCapitalized}Entity>,
|
|
589
|
+
private readonly mapper: ${entityNameCapitalized}Mapper,
|
|
590
|
+
) {}
|
|
591
|
+
|
|
592
|
+
// create
|
|
593
|
+
async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
594
|
+
const toPersist = this.mapper.toPersistence(data);
|
|
595
|
+
const created = await this.model.create(toPersist);
|
|
596
|
+
return this.mapper.toDomain(created);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// find by id
|
|
600
|
+
async findById(id: string): Promise<${entityNameCapitalized}Entity> {
|
|
601
|
+
const record = await this.model.findById(id);
|
|
602
|
+
|
|
603
|
+
if (!record) {
|
|
604
|
+
throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return this.mapper.toDomain(record);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// update
|
|
611
|
+
async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
612
|
+
const toUpdate = this.mapper.toUpdatePersistence(data);
|
|
613
|
+
const updated = await this.model.findByIdAndUpdate(id, toUpdate, { new: true });
|
|
614
|
+
return this.mapper.toDomain(updated);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// find all
|
|
618
|
+
async findAll(): Promise<${entityNameCapitalized}Entity[]> {
|
|
619
|
+
const records = await this.model.find();
|
|
620
|
+
return records.map(record => this.mapper.toDomain(record));
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// delete
|
|
624
|
+
async delete(id: string): Promise<void> {
|
|
625
|
+
await this.model.findByIdAndDelete(id);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
`,
|
|
629
|
+
});
|
|
630
|
+
break;
|
|
631
|
+
|
|
632
|
+
case "sequelize":
|
|
633
|
+
// Implémentation pour Sequelize
|
|
634
|
+
await createFile({
|
|
635
|
+
path: `${entityPath}/infrastructure/repositories/${entityNameLower}.repository.ts`,
|
|
636
|
+
contente: `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
637
|
+
import { InjectModel } from '@nestjs/sequelize';
|
|
638
|
+
import { Model } from 'sequelize-typescript';
|
|
639
|
+
import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
|
|
640
|
+
import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
|
|
641
|
+
import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
|
|
642
|
+
import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
|
|
643
|
+
|
|
644
|
+
@Injectable()
|
|
645
|
+
export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
|
|
646
|
+
constructor(
|
|
647
|
+
@InjectModel(${entityNameCapitalized}Entity)
|
|
648
|
+
private readonly model: Model<${entityNameCapitalized}Entity>,
|
|
649
|
+
private readonly mapper: ${entityNameCapitalized}Mapper,
|
|
650
|
+
) {}
|
|
651
|
+
|
|
652
|
+
// create
|
|
653
|
+
async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
654
|
+
const toPersist = this.mapper.toPersistence(data);
|
|
655
|
+
const created = await this.model.create(toPersist);
|
|
656
|
+
return this.mapper.toDomain(created);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// find by id
|
|
660
|
+
async findById(id: string): Promise<${entityNameCapitalized}Entity> {
|
|
661
|
+
const record = await this.model.findByPk(id);
|
|
662
|
+
|
|
663
|
+
if (!record) {
|
|
664
|
+
throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return this.mapper.toDomain(record);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// update
|
|
671
|
+
async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
672
|
+
const toUpdate = this.mapper.toUpdatePersistence(data);
|
|
673
|
+
const updated = await this.model.update(toUpdate, { where: { id } });
|
|
674
|
+
return this.mapper.toDomain(updated);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// find all
|
|
678
|
+
async findAll(): Promise<${entityNameCapitalized}Entity[]> {
|
|
679
|
+
const records = await this.model.findAll();
|
|
680
|
+
return records.map(record => this.mapper.toDomain(record));
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// delete
|
|
684
|
+
async delete(id: string): Promise<void> {
|
|
685
|
+
await this.model.destroy({ where: { id } });
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
`,
|
|
689
|
+
});
|
|
690
|
+
break;
|
|
691
|
+
|
|
692
|
+
default:
|
|
693
|
+
console.error("Unsupported ORM: " + orm);
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function capitalize(str) {
|
|
699
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
700
|
+
}
|
|
701
|
+
function decapitalize(str) {
|
|
702
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function formatType(type) {
|
|
706
|
+
switch (type) {
|
|
707
|
+
case "number":
|
|
708
|
+
return "number";
|
|
709
|
+
case "boolean":
|
|
710
|
+
return "boolean";
|
|
711
|
+
case "Date":
|
|
712
|
+
return "Date";
|
|
713
|
+
default:
|
|
714
|
+
return "string";
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function getFormattedType(field) {
|
|
719
|
+
if (field.name === "role" && field.type === "string") {
|
|
720
|
+
return "Role"; // 🔁 Remplacer le type string par Role
|
|
721
|
+
}
|
|
722
|
+
return formatType(field.type);
|
|
723
|
+
}
|