nestjs-ddd-cli 2.2.0 → 3.2.1
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/README.md +247 -408
- package/ddd.schema.json +111 -0
- package/dist/commands/aggregate-validator.d.ts +9 -0
- package/dist/commands/aggregate-validator.js +953 -0
- package/dist/commands/aggregate-validator.js.map +1 -0
- package/dist/commands/ai-assist.d.ts +8 -0
- package/dist/commands/ai-assist.js +337 -0
- package/dist/commands/ai-assist.js.map +1 -0
- package/dist/commands/api-contracts.d.ts +9 -0
- package/dist/commands/api-contracts.js +1368 -0
- package/dist/commands/api-contracts.js.map +1 -0
- package/dist/commands/api-docs.d.ts +8 -0
- package/dist/commands/api-docs.js +408 -0
- package/dist/commands/api-docs.js.map +1 -0
- package/dist/commands/api-versioning.d.ts +11 -0
- package/dist/commands/api-versioning.js +643 -0
- package/dist/commands/api-versioning.js.map +1 -0
- package/dist/commands/audit-logging.d.ts +9 -0
- package/dist/commands/audit-logging.js +1129 -0
- package/dist/commands/audit-logging.js.map +1 -0
- package/dist/commands/batch-generate.d.ts +10 -0
- package/dist/commands/batch-generate.js +405 -0
- package/dist/commands/batch-generate.js.map +1 -0
- package/dist/commands/caching-strategies.d.ts +9 -0
- package/dist/commands/caching-strategies.js +874 -0
- package/dist/commands/caching-strategies.js.map +1 -0
- package/dist/commands/code-analyzer.d.ts +42 -0
- package/dist/commands/code-analyzer.js +474 -0
- package/dist/commands/code-analyzer.js.map +1 -0
- package/dist/commands/database-seeding.d.ts +6 -0
- package/dist/commands/database-seeding.js +621 -0
- package/dist/commands/database-seeding.js.map +1 -0
- package/dist/commands/db-optimization.d.ts +7 -0
- package/dist/commands/db-optimization.js +687 -0
- package/dist/commands/db-optimization.js.map +1 -0
- package/dist/commands/dependency-graph.d.ts +6 -0
- package/dist/commands/dependency-graph.js +329 -0
- package/dist/commands/dependency-graph.js.map +1 -0
- package/dist/commands/doctor-enhanced.d.ts +22 -0
- package/dist/commands/doctor-enhanced.js +543 -0
- package/dist/commands/doctor-enhanced.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +151 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/env-manager.d.ts +6 -0
- package/dist/commands/env-manager.js +419 -0
- package/dist/commands/env-manager.js.map +1 -0
- package/dist/commands/event-sourcing-full.d.ts +10 -0
- package/dist/commands/event-sourcing-full.js +1107 -0
- package/dist/commands/event-sourcing-full.js.map +1 -0
- package/dist/commands/feature-flags.d.ts +9 -0
- package/dist/commands/feature-flags.js +824 -0
- package/dist/commands/feature-flags.js.map +1 -0
- package/dist/commands/filter-dsl.d.ts +10 -0
- package/dist/commands/filter-dsl.js +1407 -0
- package/dist/commands/filter-dsl.js.map +1 -0
- package/dist/commands/generate-all.js +485 -32
- package/dist/commands/generate-all.js.map +1 -1
- package/dist/commands/generate-deployment.d.ts +8 -0
- package/dist/commands/generate-deployment.js +746 -0
- package/dist/commands/generate-deployment.js.map +1 -0
- package/dist/commands/generate-domain-service.d.ts +14 -0
- package/dist/commands/generate-domain-service.js +796 -0
- package/dist/commands/generate-domain-service.js.map +1 -0
- package/dist/commands/generate-entity.js +82 -24
- package/dist/commands/generate-entity.js.map +1 -1
- package/dist/commands/generate-from-schema.d.ts +56 -0
- package/dist/commands/generate-from-schema.js +222 -0
- package/dist/commands/generate-from-schema.js.map +1 -0
- package/dist/commands/generate-orchestrator.d.ts +14 -0
- package/dist/commands/generate-orchestrator.js +887 -0
- package/dist/commands/generate-orchestrator.js.map +1 -0
- package/dist/commands/generate-repository.d.ts +14 -0
- package/dist/commands/generate-repository.js +1019 -0
- package/dist/commands/generate-repository.js.map +1 -0
- package/dist/commands/generate-shared.d.ts +4 -0
- package/dist/commands/generate-shared.js +388 -0
- package/dist/commands/generate-shared.js.map +1 -0
- package/dist/commands/generate-value-object.d.ts +32 -0
- package/dist/commands/generate-value-object.js +700 -0
- package/dist/commands/generate-value-object.js.map +1 -0
- package/dist/commands/graphql-subscriptions.d.ts +6 -0
- package/dist/commands/graphql-subscriptions.js +607 -0
- package/dist/commands/graphql-subscriptions.js.map +1 -0
- package/dist/commands/graphql-types.d.ts +5 -0
- package/dist/commands/graphql-types.js +423 -0
- package/dist/commands/graphql-types.js.map +1 -0
- package/dist/commands/health-probes-advanced.d.ts +6 -0
- package/dist/commands/health-probes-advanced.js +655 -0
- package/dist/commands/health-probes-advanced.js.map +1 -0
- package/dist/commands/i18n-setup.d.ts +10 -0
- package/dist/commands/i18n-setup.js +677 -0
- package/dist/commands/i18n-setup.js.map +1 -0
- package/dist/commands/init-config.d.ts +6 -0
- package/dist/commands/init-config.js +370 -0
- package/dist/commands/init-config.js.map +1 -0
- package/dist/commands/init-project.js +56 -6
- package/dist/commands/init-project.js.map +1 -1
- package/dist/commands/interactive-scaffold.d.ts +5 -0
- package/dist/commands/interactive-scaffold.js +271 -0
- package/dist/commands/interactive-scaffold.js.map +1 -0
- package/dist/commands/metrics-prometheus.d.ts +6 -0
- package/dist/commands/metrics-prometheus.js +681 -0
- package/dist/commands/metrics-prometheus.js.map +1 -0
- package/dist/commands/migration-engine.d.ts +6 -0
- package/dist/commands/migration-engine.js +446 -0
- package/dist/commands/migration-engine.js.map +1 -0
- package/dist/commands/migration.d.ts +12 -0
- package/dist/commands/migration.js +484 -0
- package/dist/commands/migration.js.map +1 -0
- package/dist/commands/monorepo.d.ts +8 -0
- package/dist/commands/monorepo.js +483 -0
- package/dist/commands/monorepo.js.map +1 -0
- package/dist/commands/multi-database.d.ts +5 -0
- package/dist/commands/multi-database.js +439 -0
- package/dist/commands/multi-database.js.map +1 -0
- package/dist/commands/observability-tracing.d.ts +10 -0
- package/dist/commands/observability-tracing.js +740 -0
- package/dist/commands/observability-tracing.js.map +1 -0
- package/dist/commands/openapi-export.d.ts +8 -0
- package/dist/commands/openapi-export.js +359 -0
- package/dist/commands/openapi-export.js.map +1 -0
- package/dist/commands/perf-analyzer.d.ts +8 -0
- package/dist/commands/perf-analyzer.js +423 -0
- package/dist/commands/perf-analyzer.js.map +1 -0
- package/dist/commands/rate-limiting.d.ts +10 -0
- package/dist/commands/rate-limiting.js +953 -0
- package/dist/commands/rate-limiting.js.map +1 -0
- package/dist/commands/recipe-plugin.d.ts +56 -0
- package/dist/commands/recipe-plugin.js +315 -0
- package/dist/commands/recipe-plugin.js.map +1 -0
- package/dist/commands/recipe.d.ts +6 -0
- package/dist/commands/recipe.js +3941 -0
- package/dist/commands/recipe.js.map +1 -0
- package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
- package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
- package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
- package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
- package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
- package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
- package/dist/commands/recipes/index.d.ts +7 -0
- package/dist/commands/recipes/index.js +24 -0
- package/dist/commands/recipes/index.js.map +1 -0
- package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
- package/dist/commands/recipes/message-queue.recipe.js +706 -0
- package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
- package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
- package/dist/commands/recipes/middleware.recipe.js +383 -0
- package/dist/commands/recipes/middleware.recipe.js.map +1 -0
- package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
- package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
- package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
- package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
- package/dist/commands/recipes/oauth2.recipe.js +472 -0
- package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
- package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
- package/dist/commands/recipes/websocket.recipe.js +453 -0
- package/dist/commands/recipes/websocket.recipe.js.map +1 -0
- package/dist/commands/resilience-patterns.d.ts +13 -0
- package/dist/commands/resilience-patterns.js +1029 -0
- package/dist/commands/resilience-patterns.js.map +1 -0
- package/dist/commands/security-patterns.d.ts +11 -0
- package/dist/commands/security-patterns.js +2233 -0
- package/dist/commands/security-patterns.js.map +1 -0
- package/dist/commands/template-debug.d.ts +27 -0
- package/dist/commands/template-debug.js +388 -0
- package/dist/commands/template-debug.js.map +1 -0
- package/dist/commands/test-factory-full.d.ts +9 -0
- package/dist/commands/test-factory-full.js +1570 -0
- package/dist/commands/test-factory-full.js.map +1 -0
- package/dist/commands/test-scaffold.d.ts +7 -0
- package/dist/commands/test-scaffold.js +621 -0
- package/dist/commands/test-scaffold.js.map +1 -0
- package/dist/index.js +1088 -0
- package/dist/index.js.map +1 -1
- package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
- package/dist/templates/ai-context/conventions.md.hbs +154 -0
- package/dist/templates/command/create-command.hbs +6 -14
- package/dist/templates/command/delete-command.hbs +19 -0
- package/dist/templates/command/update-command.hbs +24 -0
- package/dist/templates/controller/controller.hbs +64 -17
- package/dist/templates/dto/create-dto.hbs +29 -5
- package/dist/templates/dto/filter-dto.hbs +52 -0
- package/dist/templates/dto/filter-query.dto.hbs +148 -0
- package/dist/templates/dto/paginated-response.dto.hbs +29 -0
- package/dist/templates/dto/pagination-query.dto.hbs +30 -0
- package/dist/templates/dto/response-dto.hbs +38 -0
- package/dist/templates/dto/update-dto.hbs +11 -0
- package/dist/templates/entity/entity.hbs +32 -1
- package/dist/templates/event/domain-event.hbs +33 -7
- package/dist/templates/event/event-handler.hbs +40 -0
- package/dist/templates/exception/base-exceptions.hbs +69 -0
- package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
- package/dist/templates/mapper/mapper.hbs +49 -24
- package/dist/templates/module/module.hbs +34 -10
- package/dist/templates/orm-entity/orm-entity.hbs +63 -12
- package/dist/templates/prisma/prisma-mapper.hbs +71 -0
- package/dist/templates/prisma/prisma-repository.hbs +114 -0
- package/dist/templates/prisma/prisma-schema.hbs +20 -0
- package/dist/templates/prisma/prisma-service.hbs +51 -0
- package/dist/templates/query/get-all.query.hbs +50 -0
- package/dist/templates/query/get-by-id.query.hbs +31 -0
- package/dist/templates/repository/repository.hbs +55 -13
- package/dist/templates/resolver/graphql-input.hbs +54 -0
- package/dist/templates/resolver/graphql-type.hbs +58 -0
- package/dist/templates/resolver/pagination-args.hbs +33 -0
- package/dist/templates/resolver/resolver.hbs +62 -0
- package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
- package/dist/templates/shared/query-builder.util.hbs +218 -0
- package/dist/templates/test/controller.spec.hbs +124 -0
- package/dist/templates/test/repository.spec.hbs +158 -0
- package/dist/templates/test/usecase.spec.hbs +116 -0
- package/dist/templates/usecase/create-usecase.hbs +19 -7
- package/dist/templates/usecase/delete-usecase.hbs +17 -0
- package/dist/templates/usecase/update-usecase.hbs +31 -0
- package/dist/utils/config.utils.d.ts +45 -0
- package/dist/utils/config.utils.js +211 -0
- package/dist/utils/config.utils.js.map +1 -0
- package/dist/utils/error.utils.d.ts +145 -0
- package/dist/utils/error.utils.js +422 -0
- package/dist/utils/error.utils.js.map +1 -0
- package/dist/utils/field.utils.d.ts +54 -0
- package/dist/utils/field.utils.js +389 -0
- package/dist/utils/field.utils.js.map +1 -0
- package/dist/utils/file.utils.d.ts +19 -8
- package/dist/utils/file.utils.js +135 -4
- package/dist/utils/file.utils.js.map +1 -1
- package/dist/utils/idempotency.utils.d.ts +123 -0
- package/dist/utils/idempotency.utils.js +444 -0
- package/dist/utils/idempotency.utils.js.map +1 -0
- package/dist/utils/naming.utils.js +26 -3
- package/dist/utils/naming.utils.js.map +1 -1
- package/dist/utils/performance.utils.d.ts +37 -0
- package/dist/utils/performance.utils.js +158 -0
- package/dist/utils/performance.utils.js.map +1 -0
- package/dist/utils/relation.utils.d.ts +92 -0
- package/dist/utils/relation.utils.js +388 -0
- package/dist/utils/relation.utils.js.map +1 -0
- package/dist/utils/rollback.utils.d.ts +49 -0
- package/dist/utils/rollback.utils.js +306 -0
- package/dist/utils/rollback.utils.js.map +1 -0
- package/dist/utils/schema.utils.d.ts +123 -0
- package/dist/utils/schema.utils.js +419 -0
- package/dist/utils/schema.utils.js.map +1 -0
- package/dist/utils/security.utils.d.ts +57 -0
- package/dist/utils/security.utils.js +315 -0
- package/dist/utils/security.utils.js.map +1 -0
- package/dist/utils/template-engine.utils.d.ts +80 -0
- package/dist/utils/template-engine.utils.js +463 -0
- package/dist/utils/template-engine.utils.js.map +1 -0
- package/dist/utils/validation-registry.utils.d.ts +160 -0
- package/dist/utils/validation-registry.utils.js +526 -0
- package/dist/utils/validation-registry.utils.js.map +1 -0
- package/package.json +3 -1
|
@@ -1,24 +1,75 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Column,
|
|
3
|
+
CreateDateColumn,
|
|
4
|
+
DeleteDateColumn,
|
|
5
|
+
Entity,
|
|
6
|
+
PrimaryGeneratedColumn,
|
|
7
|
+
UpdateDateColumn,
|
|
8
|
+
{{#if hasRelations}}
|
|
9
|
+
OneToOne,
|
|
10
|
+
OneToMany,
|
|
11
|
+
ManyToOne,
|
|
12
|
+
ManyToMany,
|
|
13
|
+
JoinColumn,
|
|
14
|
+
JoinTable,
|
|
15
|
+
{{/if}}
|
|
16
|
+
} from "typeorm";
|
|
17
|
+
{{#if relationImports}}
|
|
18
|
+
{{{relationImports}}}
|
|
19
|
+
{{/if}}
|
|
2
20
|
|
|
3
21
|
@Entity("{{tableName}}")
|
|
4
22
|
export class {{entityNamePascal}}OrmEntity {
|
|
5
23
|
@PrimaryGeneratedColumn("uuid")
|
|
6
|
-
id
|
|
24
|
+
id: string;
|
|
7
25
|
|
|
8
|
-
|
|
9
|
-
|
|
26
|
+
{{#if hasFields}}
|
|
27
|
+
{{#each fields}}
|
|
28
|
+
{{#if this.isRelation}}
|
|
29
|
+
{{#if (eq this.relationType 'OneToOne')}}
|
|
30
|
+
@OneToOne(() => {{this.relationTarget}}OrmEntity{{#if this.relationInverse}}, ({{lowercase this.relationTarget}}) => {{lowercase this.relationTarget}}.{{this.camelCase}}{{/if}})
|
|
31
|
+
@JoinColumn()
|
|
32
|
+
{{this.camelCase}}{{#if this.isOptional}}?{{/if}}: {{this.relationTarget}}OrmEntity;
|
|
10
33
|
|
|
11
|
-
|
|
12
|
-
|
|
34
|
+
{{/if}}
|
|
35
|
+
{{#if (eq this.relationType 'OneToMany')}}
|
|
36
|
+
@OneToMany(() => {{this.relationTarget}}OrmEntity, ({{lowercase this.relationTarget}}) => {{lowercase this.relationTarget}}.{{#if this.relationInverse}}{{this.relationInverse}}{{else}}{{this.camelCase}}{{/if}})
|
|
37
|
+
{{this.camelCase}}: {{this.relationTarget}}OrmEntity[];
|
|
13
38
|
|
|
14
|
-
|
|
15
|
-
|
|
39
|
+
{{/if}}
|
|
40
|
+
{{#if (eq this.relationType 'ManyToOne')}}
|
|
41
|
+
@ManyToOne(() => {{this.relationTarget}}OrmEntity{{#if this.relationInverse}}, ({{lowercase this.relationTarget}}) => {{lowercase this.relationTarget}}.{{this.relationInverse}}{{/if}})
|
|
42
|
+
@JoinColumn({ name: "{{this.snakeCase}}_id" })
|
|
43
|
+
{{this.camelCase}}{{#if this.isOptional}}?{{/if}}: {{this.relationTarget}}OrmEntity;
|
|
16
44
|
|
|
17
|
-
|
|
18
|
-
|
|
45
|
+
{{/if}}
|
|
46
|
+
{{#if (eq this.relationType 'ManyToMany')}}
|
|
47
|
+
@ManyToMany(() => {{this.relationTarget}}OrmEntity)
|
|
48
|
+
@JoinTable()
|
|
49
|
+
{{this.camelCase}}: {{this.relationTarget}}OrmEntity[];
|
|
19
50
|
|
|
51
|
+
{{/if}}
|
|
52
|
+
{{else}}
|
|
53
|
+
@Column({{#if this.isUnique}}{ unique: true{{#if this.isOptional}}, nullable: true{{/if}} }{{else}}{{#if this.isOptional}}{ nullable: true }{{/if}}{{/if}})
|
|
54
|
+
{{this.snakeCase}}{{#if this.isOptional}}?{{/if}}: {{this.tsType}};
|
|
55
|
+
|
|
56
|
+
{{/if}}
|
|
57
|
+
{{/each}}
|
|
58
|
+
{{else}}
|
|
20
59
|
// Add your columns here
|
|
21
60
|
// Example:
|
|
22
61
|
// @Column()
|
|
23
|
-
//
|
|
24
|
-
}
|
|
62
|
+
// column_name: string;
|
|
63
|
+
{{/if}}
|
|
64
|
+
@Column({ name: "is_active", default: true })
|
|
65
|
+
isActive: boolean;
|
|
66
|
+
|
|
67
|
+
@CreateDateColumn({ name: "created_at" })
|
|
68
|
+
createdAt: Date;
|
|
69
|
+
|
|
70
|
+
@UpdateDateColumn({ name: "updated_at" })
|
|
71
|
+
updatedAt: Date;
|
|
72
|
+
|
|
73
|
+
@DeleteDateColumn({ name: "deleted_at" })
|
|
74
|
+
deletedAt?: Date;
|
|
75
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { {{entityNamePascal}} as Prisma{{entityNamePascal}} } from "@prisma/client";
|
|
3
|
+
import { {{entityNamePascal}}Entity } from "@modules/{{moduleNameKebab}}/application/domain/entities/{{entityNameKebab}}.entity";
|
|
4
|
+
import { {{entityNamePascal}}ResponseDto } from "@modules/{{moduleNameKebab}}/application/dto/responses/{{entityNameKebab}}.response.dto";
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class {{entityNamePascal}}Mapper {
|
|
8
|
+
toDomainEntity(prismaEntity: Prisma{{entityNamePascal}}): {{entityNamePascal}}Entity {
|
|
9
|
+
return new {{entityNamePascal}}Entity({
|
|
10
|
+
id: prismaEntity.id,
|
|
11
|
+
isActive: prismaEntity.is_active,
|
|
12
|
+
createdAt: prismaEntity.created_at,
|
|
13
|
+
updatedAt: prismaEntity.updated_at,
|
|
14
|
+
deletedAt: prismaEntity.deleted_at ?? undefined,
|
|
15
|
+
{{#if hasFields}}
|
|
16
|
+
{{#each fields}}
|
|
17
|
+
{{this.camelCase}}: prismaEntity.{{this.snakeCase}},
|
|
18
|
+
{{/each}}
|
|
19
|
+
{{else}}
|
|
20
|
+
// Map your Prisma fields to domain entity
|
|
21
|
+
// propertyName: prismaEntity.property_name,
|
|
22
|
+
{{/if}}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
toPrismaCreate(entity: {{entityNamePascal}}Entity): any {
|
|
27
|
+
return {
|
|
28
|
+
{{#if hasFields}}
|
|
29
|
+
{{#each fields}}
|
|
30
|
+
{{this.snakeCase}}: entity.{{this.camelCase}},
|
|
31
|
+
{{/each}}
|
|
32
|
+
{{else}}
|
|
33
|
+
// Map your domain entity to Prisma create input
|
|
34
|
+
// property_name: entity.propertyName,
|
|
35
|
+
{{/if}}
|
|
36
|
+
is_active: entity.isActive ?? true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
toPrismaUpdate(entity: {{entityNamePascal}}Entity): any {
|
|
41
|
+
return {
|
|
42
|
+
{{#if hasFields}}
|
|
43
|
+
{{#each fields}}
|
|
44
|
+
{{this.snakeCase}}: entity.{{this.camelCase}},
|
|
45
|
+
{{/each}}
|
|
46
|
+
{{else}}
|
|
47
|
+
// Map your domain entity to Prisma update input
|
|
48
|
+
// property_name: entity.propertyName,
|
|
49
|
+
{{/if}}
|
|
50
|
+
is_active: entity.isActive,
|
|
51
|
+
updated_at: new Date(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
toResponseDto(entity: {{entityNamePascal}}Entity): {{entityNamePascal}}ResponseDto {
|
|
56
|
+
return new {{entityNamePascal}}ResponseDto({
|
|
57
|
+
id: entity.id!,
|
|
58
|
+
isActive: entity.isActive!,
|
|
59
|
+
createdAt: entity.createdAt!,
|
|
60
|
+
updatedAt: entity.updatedAt!,
|
|
61
|
+
{{#if hasFields}}
|
|
62
|
+
{{#each fields}}
|
|
63
|
+
{{this.camelCase}}: entity.{{this.camelCase}},
|
|
64
|
+
{{/each}}
|
|
65
|
+
{{else}}
|
|
66
|
+
// Map your response fields
|
|
67
|
+
// propertyName: entity.propertyName,
|
|
68
|
+
{{/if}}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import { PrismaService } from "@prisma/prisma.service";
|
|
3
|
+
import { {{entityNamePascal}}Entity } from "@modules/{{moduleNameKebab}}/application/domain/entities/{{entityNameKebab}}.entity";
|
|
4
|
+
import { {{entityNamePascal}}Mapper } from "@modules/{{moduleNameKebab}}/infrastructure/mappers/{{entityNameKebab}}.mapper";
|
|
5
|
+
import { Prisma } from "@prisma/client";
|
|
6
|
+
|
|
7
|
+
export interface PaginationOptions {
|
|
8
|
+
skip: number;
|
|
9
|
+
take: number;
|
|
10
|
+
orderBy: string;
|
|
11
|
+
orderDirection: "asc" | "desc";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Injectable()
|
|
15
|
+
export class {{entityNamePascal}}Repository {
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly prisma: PrismaService,
|
|
18
|
+
private readonly mapper: {{entityNamePascal}}Mapper,
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
async create(entity: {{entityNamePascal}}Entity): Promise<{{entityNamePascal}}Entity> {
|
|
22
|
+
const data = this.mapper.toPrismaCreate(entity);
|
|
23
|
+
const created = await this.prisma.{{entityNameCamel}}.create({ data });
|
|
24
|
+
return this.mapper.toDomainEntity(created);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async findById(id: string): Promise<{{entityNamePascal}}Entity | null> {
|
|
28
|
+
const record = await this.prisma.{{entityNameCamel}}.findFirst({
|
|
29
|
+
where: { id, deleted_at: null },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (!record) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return this.mapper.toDomainEntity(record);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async findAll(): Promise<{{entityNamePascal}}Entity[]> {
|
|
40
|
+
const records = await this.prisma.{{entityNameCamel}}.findMany({
|
|
41
|
+
where: { deleted_at: null },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return records.map((record) => this.mapper.toDomainEntity(record));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async findAllPaginated(
|
|
48
|
+
options: PaginationOptions,
|
|
49
|
+
): Promise<[{{entityNamePascal}}Entity[], number]> {
|
|
50
|
+
const { skip, take, orderBy, orderDirection } = options;
|
|
51
|
+
|
|
52
|
+
const [records, total] = await Promise.all([
|
|
53
|
+
this.prisma.{{entityNameCamel}}.findMany({
|
|
54
|
+
where: { deleted_at: null },
|
|
55
|
+
skip,
|
|
56
|
+
take,
|
|
57
|
+
orderBy: { [orderBy]: orderDirection },
|
|
58
|
+
}),
|
|
59
|
+
this.prisma.{{entityNameCamel}}.count({
|
|
60
|
+
where: { deleted_at: null },
|
|
61
|
+
}),
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
const entities = records.map((record) => this.mapper.toDomainEntity(record));
|
|
65
|
+
return [entities, total];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async update(id: string, entity: {{entityNamePascal}}Entity): Promise<{{entityNamePascal}}Entity> {
|
|
69
|
+
const data = this.mapper.toPrismaUpdate(entity);
|
|
70
|
+
const updated = await this.prisma.{{entityNameCamel}}.update({
|
|
71
|
+
where: { id },
|
|
72
|
+
data,
|
|
73
|
+
});
|
|
74
|
+
return this.mapper.toDomainEntity(updated);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async delete(id: string): Promise<void> {
|
|
78
|
+
await this.prisma.{{entityNameCamel}}.update({
|
|
79
|
+
where: { id },
|
|
80
|
+
data: {
|
|
81
|
+
deleted_at: new Date(),
|
|
82
|
+
is_active: false,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async hardDelete(id: string): Promise<void> {
|
|
88
|
+
await this.prisma.{{entityNameCamel}}.delete({
|
|
89
|
+
where: { id },
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async exists(id: string): Promise<boolean> {
|
|
94
|
+
const count = await this.prisma.{{entityNameCamel}}.count({
|
|
95
|
+
where: { id, deleted_at: null },
|
|
96
|
+
});
|
|
97
|
+
return count > 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async findByField<K extends keyof Prisma.{{entityNamePascal}}WhereInput>(
|
|
101
|
+
field: K,
|
|
102
|
+
value: Prisma.{{entityNamePascal}}WhereInput[K],
|
|
103
|
+
): Promise<{{entityNamePascal}}Entity | null> {
|
|
104
|
+
const record = await this.prisma.{{entityNameCamel}}.findFirst({
|
|
105
|
+
where: { [field]: value, deleted_at: null },
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (!record) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.mapper.toDomainEntity(record);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Add this model to your schema.prisma file
|
|
2
|
+
|
|
3
|
+
model {{entityNamePascal}} {
|
|
4
|
+
id String @id @default(uuid())
|
|
5
|
+
{{#if hasFields}}
|
|
6
|
+
{{#each fields}}
|
|
7
|
+
{{this.snakeCase}} {{this.prismaType}}{{#if this.isOptional}}?{{/if}}{{#if this.isUnique}} @unique{{/if}}
|
|
8
|
+
{{/each}}
|
|
9
|
+
{{else}}
|
|
10
|
+
// Add your fields here
|
|
11
|
+
// name String
|
|
12
|
+
// email String @unique
|
|
13
|
+
{{/if}}
|
|
14
|
+
is_active Boolean @default(true)
|
|
15
|
+
created_at DateTime @default(now())
|
|
16
|
+
updated_at DateTime @updatedAt
|
|
17
|
+
deleted_at DateTime?
|
|
18
|
+
|
|
19
|
+
@@map("{{tableName}}")
|
|
20
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
|
|
2
|
+
import { PrismaClient } from "@prisma/client";
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
|
|
6
|
+
constructor() {
|
|
7
|
+
super({
|
|
8
|
+
log: process.env.NODE_ENV === "development"
|
|
9
|
+
? ["query", "info", "warn", "error"]
|
|
10
|
+
: ["error"],
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async onModuleInit() {
|
|
15
|
+
await this.$connect();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async onModuleDestroy() {
|
|
19
|
+
await this.$disconnect();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execute operations in a transaction
|
|
24
|
+
*/
|
|
25
|
+
async executeInTransaction<T>(
|
|
26
|
+
fn: (prisma: Omit<PrismaClient, "$connect" | "$disconnect" | "$on" | "$transaction" | "$use">) => Promise<T>,
|
|
27
|
+
): Promise<T> {
|
|
28
|
+
return this.$transaction(fn);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Clean database (useful for testing)
|
|
33
|
+
*/
|
|
34
|
+
async cleanDatabase() {
|
|
35
|
+
if (process.env.NODE_ENV !== "test") {
|
|
36
|
+
throw new Error("cleanDatabase can only be called in test environment");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const models = Reflect.ownKeys(this).filter(
|
|
40
|
+
(key) => typeof key === "string" && !key.startsWith("_") && !key.startsWith("$"),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
for (const model of models) {
|
|
44
|
+
try {
|
|
45
|
+
await (this as any)[model].deleteMany();
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Ignore if model doesn't support deleteMany
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { IQueryHandler, QueryHandler } from "@nestjs/cqrs";
|
|
2
|
+
import { {{entityNamePascal}}Repository } from "@modules/{{moduleNameKebab}}/infrastructure/repositories/{{entityNameKebab}}.repository";
|
|
3
|
+
import { {{entityNamePascal}}Mapper } from "@modules/{{moduleNameKebab}}/infrastructure/mappers/{{entityNameKebab}}.mapper";
|
|
4
|
+
import { {{entityNamePascal}}ResponseDto } from "@modules/{{moduleNameKebab}}/application/dto/responses/{{entityNameKebab}}.response.dto";
|
|
5
|
+
import { PaginatedResponseDto } from "@modules/{{moduleNameKebab}}/application/dto/responses/paginated.response.dto";
|
|
6
|
+
import { PaginationQueryDto } from "@modules/{{moduleNameKebab}}/application/dto/requests/pagination.query.dto";
|
|
7
|
+
|
|
8
|
+
export class GetAll{{entityNamePluralPascal}}Query {
|
|
9
|
+
constructor(public readonly pagination: PaginationQueryDto) {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@QueryHandler(GetAll{{entityNamePluralPascal}}Query)
|
|
13
|
+
export class GetAll{{entityNamePluralPascal}}Handler
|
|
14
|
+
implements IQueryHandler<GetAll{{entityNamePluralPascal}}Query, PaginatedResponseDto<{{entityNamePascal}}ResponseDto>>
|
|
15
|
+
{
|
|
16
|
+
constructor(
|
|
17
|
+
private readonly repository: {{entityNamePascal}}Repository,
|
|
18
|
+
private readonly mapper: {{entityNamePascal}}Mapper,
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
async execute(
|
|
22
|
+
query: GetAll{{entityNamePluralPascal}}Query,
|
|
23
|
+
): Promise<PaginatedResponseDto<{{entityNamePascal}}ResponseDto>> {
|
|
24
|
+
const { pagination } = query;
|
|
25
|
+
const { page = 1, limit = 10, sortBy = "createdAt", sortOrder = "DESC" } = pagination;
|
|
26
|
+
|
|
27
|
+
const skip = (page - 1) * limit;
|
|
28
|
+
|
|
29
|
+
const [entities, total] = await this.repository.findAllPaginated({
|
|
30
|
+
skip,
|
|
31
|
+
take: limit,
|
|
32
|
+
orderBy: sortBy,
|
|
33
|
+
orderDirection: sortOrder,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const items = entities.map((entity) => this.mapper.toResponseDto(entity));
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
items,
|
|
40
|
+
meta: {
|
|
41
|
+
total,
|
|
42
|
+
page,
|
|
43
|
+
limit,
|
|
44
|
+
totalPages: Math.ceil(total / limit),
|
|
45
|
+
hasNextPage: page * limit < total,
|
|
46
|
+
hasPreviousPage: page > 1,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IQueryHandler, QueryHandler } from "@nestjs/cqrs";
|
|
2
|
+
import { NotFoundException } from "@nestjs/common";
|
|
3
|
+
import { {{entityNamePascal}}Repository } from "@modules/{{moduleNameKebab}}/infrastructure/repositories/{{entityNameKebab}}.repository";
|
|
4
|
+
import { {{entityNamePascal}}Mapper } from "@modules/{{moduleNameKebab}}/infrastructure/mappers/{{entityNameKebab}}.mapper";
|
|
5
|
+
import { {{entityNamePascal}}ResponseDto } from "@modules/{{moduleNameKebab}}/application/dto/responses/{{entityNameKebab}}.response.dto";
|
|
6
|
+
|
|
7
|
+
export class Get{{entityNamePascal}}ByIdQuery {
|
|
8
|
+
constructor(public readonly id: string) {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@QueryHandler(Get{{entityNamePascal}}ByIdQuery)
|
|
12
|
+
export class Get{{entityNamePascal}}ByIdHandler
|
|
13
|
+
implements IQueryHandler<Get{{entityNamePascal}}ByIdQuery, {{entityNamePascal}}ResponseDto>
|
|
14
|
+
{
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly repository: {{entityNamePascal}}Repository,
|
|
17
|
+
private readonly mapper: {{entityNamePascal}}Mapper,
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
async execute(query: Get{{entityNamePascal}}ByIdQuery): Promise<{{entityNamePascal}}ResponseDto> {
|
|
21
|
+
const { id } = query;
|
|
22
|
+
|
|
23
|
+
const entity = await this.repository.findById(id);
|
|
24
|
+
|
|
25
|
+
if (!entity) {
|
|
26
|
+
throw new NotFoundException(`{{entityNamePascal}} with id '${id}' not found`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return this.mapper.toResponseDto(entity);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
1
2
|
import { InjectRepository } from "@nestjs/typeorm";
|
|
2
|
-
import { Repository } from "typeorm";
|
|
3
|
+
import { Repository, FindOptionsOrder } from "typeorm";
|
|
3
4
|
import { {{entityNamePascal}}Mapper } from "@modules/{{moduleNameKebab}}/infrastructure/mappers/{{entityNameKebab}}.mapper";
|
|
4
5
|
import { {{entityNamePascal}}OrmEntity } from "@modules/{{moduleNameKebab}}/infrastructure/orm-entities/{{entityNameKebab}}.orm-entity";
|
|
5
6
|
import { {{entityNamePascal}}Entity } from "@modules/{{moduleNameKebab}}/domain/entities/{{entityNameKebab}}.entity";
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
export interface PaginationOptions {
|
|
9
|
+
skip: number;
|
|
10
|
+
take: number;
|
|
11
|
+
orderBy: string;
|
|
12
|
+
orderDirection: "ASC" | "DESC";
|
|
13
|
+
}
|
|
7
14
|
|
|
8
15
|
@Injectable()
|
|
9
16
|
export class {{entityNamePascal}}Repository {
|
|
@@ -21,34 +28,69 @@ export class {{entityNamePascal}}Repository {
|
|
|
21
28
|
|
|
22
29
|
async findById(id: string): Promise<{{entityNamePascal}}Entity | null> {
|
|
23
30
|
const ormEntity = await this.repository.findOne({
|
|
24
|
-
where: { id },
|
|
31
|
+
where: { id, deletedAt: null } as any,
|
|
25
32
|
});
|
|
33
|
+
|
|
26
34
|
if (!ormEntity) {
|
|
27
35
|
return null;
|
|
28
36
|
}
|
|
37
|
+
|
|
29
38
|
return this.mapper.toDomainEntity(ormEntity);
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
async findAll(): Promise<{{entityNamePascal}}Entity[]> {
|
|
33
|
-
const ormEntities = await this.repository.find(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return ormEntities.map((ormEntity) =>
|
|
42
|
+
const ormEntities = await this.repository.find({
|
|
43
|
+
where: { deletedAt: null } as any,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return ormEntities.map((ormEntity) => this.mapper.toDomainEntity(ormEntity));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async findAllPaginated(
|
|
50
|
+
options: PaginationOptions,
|
|
51
|
+
): Promise<[{{entityNamePascal}}Entity[], number]> {
|
|
52
|
+
const { skip, take, orderBy, orderDirection } = options;
|
|
53
|
+
|
|
54
|
+
const order: FindOptionsOrder<{{entityNamePascal}}OrmEntity> = {
|
|
55
|
+
[orderBy]: orderDirection,
|
|
56
|
+
} as FindOptionsOrder<{{entityNamePascal}}OrmEntity>;
|
|
57
|
+
|
|
58
|
+
const [ormEntities, total] = await this.repository.findAndCount({
|
|
59
|
+
where: { deletedAt: null } as any,
|
|
60
|
+
skip,
|
|
61
|
+
take,
|
|
62
|
+
order,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const entities = ormEntities.map((ormEntity) =>
|
|
38
66
|
this.mapper.toDomainEntity(ormEntity),
|
|
39
67
|
);
|
|
68
|
+
|
|
69
|
+
return [entities, total];
|
|
40
70
|
}
|
|
41
71
|
|
|
42
|
-
async update(
|
|
43
|
-
id: string,
|
|
44
|
-
entity: {{entityNamePascal}}Entity,
|
|
45
|
-
): Promise<{{entityNamePascal}}Entity> {
|
|
72
|
+
async update(id: string, entity: {{entityNamePascal}}Entity): Promise<{{entityNamePascal}}Entity> {
|
|
46
73
|
const ormEntity = this.mapper.toOrmEntity(entity);
|
|
47
74
|
await this.repository.update(id, ormEntity);
|
|
48
75
|
return entity;
|
|
49
76
|
}
|
|
50
77
|
|
|
51
78
|
async delete(id: string): Promise<void> {
|
|
79
|
+
// Soft delete
|
|
80
|
+
await this.repository.update(id, {
|
|
81
|
+
deletedAt: new Date(),
|
|
82
|
+
isActive: false,
|
|
83
|
+
} as any);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async hardDelete(id: string): Promise<void> {
|
|
52
87
|
await this.repository.delete(id);
|
|
53
88
|
}
|
|
54
|
-
|
|
89
|
+
|
|
90
|
+
async exists(id: string): Promise<boolean> {
|
|
91
|
+
const count = await this.repository.count({
|
|
92
|
+
where: { id, deletedAt: null } as any,
|
|
93
|
+
});
|
|
94
|
+
return count > 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Field, InputType } from "@nestjs/graphql";
|
|
2
|
+
import { IsNotEmpty, IsOptional, IsString, IsBoolean } from "class-validator";
|
|
3
|
+
|
|
4
|
+
@InputType()
|
|
5
|
+
export class Create{{entityNamePascal}}Input {
|
|
6
|
+
{{#if hasFields}}
|
|
7
|
+
{{#each fields}}
|
|
8
|
+
{{#unless this.isOptional}}
|
|
9
|
+
@Field()
|
|
10
|
+
@IsNotEmpty()
|
|
11
|
+
{{this.camelCase}}: {{this.tsType}};
|
|
12
|
+
|
|
13
|
+
{{/unless}}
|
|
14
|
+
{{/each}}
|
|
15
|
+
{{#each fields}}
|
|
16
|
+
{{#if this.isOptional}}
|
|
17
|
+
@Field({ nullable: true })
|
|
18
|
+
@IsOptional()
|
|
19
|
+
{{this.camelCase}}?: {{this.tsType}};
|
|
20
|
+
|
|
21
|
+
{{/if}}
|
|
22
|
+
{{/each}}
|
|
23
|
+
{{else}}
|
|
24
|
+
// Add your input fields here
|
|
25
|
+
// Example:
|
|
26
|
+
// @Field()
|
|
27
|
+
// @IsNotEmpty()
|
|
28
|
+
// @IsString()
|
|
29
|
+
// name: string;
|
|
30
|
+
{{/if}}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@InputType()
|
|
34
|
+
export class Update{{entityNamePascal}}Input {
|
|
35
|
+
{{#if hasFields}}
|
|
36
|
+
{{#each fields}}
|
|
37
|
+
@Field({ nullable: true })
|
|
38
|
+
@IsOptional()
|
|
39
|
+
{{this.camelCase}}?: {{this.tsType}};
|
|
40
|
+
|
|
41
|
+
{{/each}}
|
|
42
|
+
{{else}}
|
|
43
|
+
// Add your input fields here
|
|
44
|
+
// Example:
|
|
45
|
+
// @Field({ nullable: true })
|
|
46
|
+
// @IsOptional()
|
|
47
|
+
// @IsString()
|
|
48
|
+
// name?: string;
|
|
49
|
+
{{/if}}
|
|
50
|
+
@Field({ nullable: true })
|
|
51
|
+
@IsOptional()
|
|
52
|
+
@IsBoolean()
|
|
53
|
+
isActive?: boolean;
|
|
54
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Field, ID, ObjectType } from "@nestjs/graphql";
|
|
2
|
+
|
|
3
|
+
@ObjectType()
|
|
4
|
+
export class {{entityNamePascal}}Type {
|
|
5
|
+
@Field(() => ID)
|
|
6
|
+
id: string;
|
|
7
|
+
|
|
8
|
+
{{#if hasFields}}
|
|
9
|
+
{{#each fields}}
|
|
10
|
+
@Field({{#if this.isOptional}}{ nullable: true }{{/if}})
|
|
11
|
+
{{this.camelCase}}{{#if this.isOptional}}?{{/if}}: {{this.tsType}};
|
|
12
|
+
|
|
13
|
+
{{/each}}
|
|
14
|
+
{{else}}
|
|
15
|
+
// Add your GraphQL fields here
|
|
16
|
+
// Example:
|
|
17
|
+
// @Field()
|
|
18
|
+
// name: string;
|
|
19
|
+
{{/if}}
|
|
20
|
+
@Field()
|
|
21
|
+
isActive: boolean;
|
|
22
|
+
|
|
23
|
+
@Field()
|
|
24
|
+
createdAt: Date;
|
|
25
|
+
|
|
26
|
+
@Field()
|
|
27
|
+
updatedAt: Date;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@ObjectType()
|
|
31
|
+
export class PaginationMetaType {
|
|
32
|
+
@Field()
|
|
33
|
+
total: number;
|
|
34
|
+
|
|
35
|
+
@Field()
|
|
36
|
+
page: number;
|
|
37
|
+
|
|
38
|
+
@Field()
|
|
39
|
+
limit: number;
|
|
40
|
+
|
|
41
|
+
@Field()
|
|
42
|
+
totalPages: number;
|
|
43
|
+
|
|
44
|
+
@Field()
|
|
45
|
+
hasNextPage: boolean;
|
|
46
|
+
|
|
47
|
+
@Field()
|
|
48
|
+
hasPreviousPage: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@ObjectType()
|
|
52
|
+
export class Paginated{{entityNamePluralPascal}}Type {
|
|
53
|
+
@Field(() => [{{entityNamePascal}}Type])
|
|
54
|
+
items: {{entityNamePascal}}Type[];
|
|
55
|
+
|
|
56
|
+
@Field(() => PaginationMetaType)
|
|
57
|
+
meta: PaginationMetaType;
|
|
58
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ArgsType, Field, Int } from "@nestjs/graphql";
|
|
2
|
+
import { IsOptional, Min, Max, IsIn } from "class-validator";
|
|
3
|
+
|
|
4
|
+
@ArgsType()
|
|
5
|
+
export class PaginationArgs {
|
|
6
|
+
@Field(() => Int, { defaultValue: 1 })
|
|
7
|
+
@IsOptional()
|
|
8
|
+
@Min(1)
|
|
9
|
+
page: number = 1;
|
|
10
|
+
|
|
11
|
+
@Field(() => Int, { defaultValue: 10 })
|
|
12
|
+
@IsOptional()
|
|
13
|
+
@Min(1)
|
|
14
|
+
@Max(100)
|
|
15
|
+
limit: number = 10;
|
|
16
|
+
|
|
17
|
+
@Field({ defaultValue: "createdAt" })
|
|
18
|
+
@IsOptional()
|
|
19
|
+
sortBy: string = "createdAt";
|
|
20
|
+
|
|
21
|
+
@Field({ defaultValue: "DESC" })
|
|
22
|
+
@IsOptional()
|
|
23
|
+
@IsIn(["ASC", "DESC"])
|
|
24
|
+
sortOrder: "ASC" | "DESC" = "DESC";
|
|
25
|
+
|
|
26
|
+
get skip(): number {
|
|
27
|
+
return (this.page - 1) * this.limit;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get take(): number {
|
|
31
|
+
return this.limit;
|
|
32
|
+
}
|
|
33
|
+
}
|