@koalarx/nest-cli 3.0.2 → 3.0.4

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.
Files changed (90) hide show
  1. package/LICENSE +21 -0
  2. package/commands/new-project/index.js +70 -0
  3. package/commands/new-project/new-project.js +69 -0
  4. package/index.js +39 -0
  5. package/package.json +9 -3
  6. package/templates/startup-project/.prettierrc.json +9 -0
  7. package/templates/startup-project/eslint.config.mts +85 -0
  8. package/templates/{root-files-folders → startup-project}/package.json +1 -1
  9. package/templates/startup-project/prisma/migrations/20250320165043_init/migration.sql +19 -0
  10. package/templates/startup-project/prisma/migrations/20250320171528_aplicando_map_de_tabelas_e_colunas/migration.sql +35 -0
  11. package/templates/startup-project/prisma/migrations/20250321190248_incluindo_status_ao_person/migration.sql +2 -0
  12. package/templates/startup-project/prisma/migrations/20250326185507_incluindo_tabela_de_endereco/migration.sql +19 -0
  13. package/templates/startup-project/prisma/migrations/20250326195627_aplicando_on_delete_cascade/migration.sql +5 -0
  14. package/templates/startup-project/prisma/migrations/20250326200821_removendo_ondelete_cascade/migration.sql +5 -0
  15. package/templates/startup-project/prisma/migrations/migration_lock.toml +3 -0
  16. package/templates/startup-project/prisma/schema.prisma +39 -0
  17. package/templates/startup-project/prisma.config.ts +8 -0
  18. package/templates/startup-project/src/application/mapping/mapping.profile.ts +10 -0
  19. package/templates/startup-project/src/application/mapping/person.mapping.ts +39 -0
  20. package/templates/startup-project/src/application/person/common/persist-person.request.ts +28 -0
  21. package/templates/startup-project/src/application/person/create/create-person.handler.spec.ts +22 -0
  22. package/templates/startup-project/src/application/person/create/create-person.handler.ts +39 -0
  23. package/templates/startup-project/src/application/person/create/create-person.request.ts +11 -0
  24. package/templates/startup-project/src/application/person/create/create-person.response.ts +3 -0
  25. package/templates/startup-project/src/application/person/create/create-person.validator.ts +19 -0
  26. package/templates/startup-project/src/application/person/create-person-job/create-person-job.ts +58 -0
  27. package/templates/startup-project/src/application/person/delete/delete-person.handler.spec.ts +24 -0
  28. package/templates/startup-project/src/application/person/delete/delete-person.handler.ts +33 -0
  29. package/templates/startup-project/src/application/person/delete-inative-job/delete-inactive-job.ts +49 -0
  30. package/templates/startup-project/src/application/person/events/inactive-person/inactive-person-event.ts +3 -0
  31. package/templates/startup-project/src/application/person/events/inactive-person/inactive-person-handler.ts +27 -0
  32. package/templates/startup-project/src/application/person/events/person-event.job.ts +11 -0
  33. package/templates/startup-project/src/application/person/read/read-person.handler.spec.ts +28 -0
  34. package/templates/startup-project/src/application/person/read/read-person.handler.ts +37 -0
  35. package/templates/startup-project/src/application/person/read/read-person.response.ts +44 -0
  36. package/templates/startup-project/src/application/person/read-many/read-many-person.handler.spec.ts +67 -0
  37. package/templates/startup-project/src/application/person/read-many/read-many-person.handler.ts +47 -0
  38. package/templates/startup-project/src/application/person/read-many/read-many-person.request.ts +21 -0
  39. package/templates/startup-project/src/application/person/read-many/read-many-person.response.ts +16 -0
  40. package/templates/startup-project/src/application/person/read-many/read-many.validator.ts +16 -0
  41. package/templates/startup-project/src/application/person/update/update-person.handler.spec.ts +47 -0
  42. package/templates/startup-project/src/application/person/update/update-person.handler.ts +57 -0
  43. package/templates/startup-project/src/application/person/update/update-person.request.ts +36 -0
  44. package/templates/startup-project/src/application/person/update/update-person.validator.ts +21 -0
  45. package/templates/startup-project/src/core/env.ts +6 -0
  46. package/templates/startup-project/src/domain/dtos/read-many-person.dto.ts +16 -0
  47. package/templates/startup-project/src/domain/entities/person/person-address.ts +12 -0
  48. package/templates/startup-project/src/domain/entities/person/person-phone.ts +12 -0
  49. package/templates/startup-project/src/domain/entities/person/person.ts +24 -0
  50. package/templates/startup-project/src/domain/repositories/iperson.repository.ts +10 -0
  51. package/templates/startup-project/src/host/app.module.ts +19 -0
  52. package/templates/startup-project/src/host/controllers/controller.module.ts +16 -0
  53. package/templates/startup-project/src/host/controllers/person/create-person.controller.ts +30 -0
  54. package/templates/startup-project/src/host/controllers/person/delete-person.controller.ts +22 -0
  55. package/templates/startup-project/src/host/controllers/person/person.controller.e2e-spec.ts +158 -0
  56. package/templates/startup-project/src/host/controllers/person/person.module.ts +37 -0
  57. package/templates/startup-project/src/host/controllers/person/read-many-person.controller.ts +29 -0
  58. package/templates/startup-project/src/host/controllers/person/read-person.controller.ts +26 -0
  59. package/templates/startup-project/src/host/controllers/person/router.config.ts +9 -0
  60. package/templates/startup-project/src/host/controllers/person/update-person.controller.ts +30 -0
  61. package/templates/startup-project/src/host/main.ts +38 -0
  62. package/templates/startup-project/src/infra/database/db-transaction-context.ts +24 -0
  63. package/templates/startup-project/src/infra/database/repositories/person.repository.ts +54 -0
  64. package/templates/startup-project/src/infra/database/repositories/repositories.module.ts +15 -0
  65. package/templates/startup-project/src/infra/infra.module.ts +8 -0
  66. package/templates/startup-project/src/test/create-e2e-test-app.ts +27 -0
  67. package/templates/startup-project/src/test/create-unit-test-app.ts +24 -0
  68. package/templates/startup-project/src/test/mockup/person/create-person-request.mockup.ts +9 -0
  69. package/templates/startup-project/src/test/repositories/person.repository.ts +36 -0
  70. package/templates/startup-project/src/test/setup-e2e.ts +12 -0
  71. package/templates/startup-project/tsconfig.app.json +8 -0
  72. package/tsconfig.tsbuildinfo +1 -0
  73. package/CHANGELOG.md +0 -11
  74. package/src/commands/new-project/index.ts +0 -99
  75. package/src/commands/new-project/new-project.ts +0 -91
  76. package/src/index.ts +0 -51
  77. package/tsconfig.json +0 -14
  78. /package/templates/{root-files-folders → startup-project}/.dockerignore +0 -0
  79. /package/templates/{root-files-folders → startup-project}/.gitattributes +0 -0
  80. /package/templates/{root-files-folders → startup-project}/.vscode/launch.json +0 -0
  81. /package/templates/{root-files-folders → startup-project}/.vscode/mcp.json +0 -0
  82. /package/templates/{root-files-folders → startup-project}/.vscode/settings.json +0 -0
  83. /package/templates/{root-files-folders → startup-project}/.vscode/tasks.json +0 -0
  84. /package/templates/{root-files-folders → startup-project}/Dockerfile +0 -0
  85. /package/templates/{root-files-folders → startup-project}/README.md +0 -0
  86. /package/templates/{root-files-folders → startup-project}/bunfig.toml +0 -0
  87. /package/templates/{root-files-folders → startup-project}/nest-cli.json +0 -0
  88. /package/templates/{root-files-folders → startup-project}/tsconfig.build.json +0 -0
  89. /package/templates/{root-files-folders → startup-project}/tsconfig.json +0 -0
  90. /package/templates/{root-files-folders → startup-project}/tsconfig.prisma.json +0 -0
@@ -0,0 +1,58 @@
1
+ import { IPersonRepository } from '@/domain/repositories/iperson.repository'
2
+ import {
3
+ CronJobHandlerBase,
4
+ CronJobResponse,
5
+ CronJobSettings,
6
+ } from '@koalarx/nest/core/backgroud-services/cron-service/cron-job.handler.base'
7
+ import { EventQueue } from '@koalarx/nest/core/backgroud-services/event-service/event-queue'
8
+ import { ok } from '@koalarx/nest/core/request-overflow/request-result'
9
+ import { ILoggingService } from '@koalarx/nest/services/logging/ilogging.service'
10
+ import { IRedLockService } from '@koalarx/nest/services/redlock/ired-lock.service'
11
+ import { Injectable } from '@nestjs/common'
12
+ import { CreatePersonHandler } from '../create/create-person.handler'
13
+ import { InactivePersonEvent } from '../events/inactive-person/inactive-person-event'
14
+ import { PersonEventJob } from '../events/person-event.job'
15
+
16
+ @Injectable()
17
+ export class CreatePersonJob extends CronJobHandlerBase {
18
+ constructor(
19
+ redlockService: IRedLockService,
20
+ loggingService: ILoggingService,
21
+ private readonly createPerson: CreatePersonHandler,
22
+ private readonly repository: IPersonRepository,
23
+ ) {
24
+ super(redlockService, loggingService)
25
+ }
26
+
27
+ protected async settings(): Promise<CronJobSettings> {
28
+ return {
29
+ isActive: true,
30
+ timeInMinutes: 1,
31
+ }
32
+ }
33
+
34
+ protected async run(): Promise<CronJobResponse> {
35
+ const result = await this.createPerson.handle({
36
+ name: 'John Doe',
37
+ phones: [{ phone: '22999999999' }],
38
+ address: { address: 'Street 1' },
39
+ })
40
+
41
+ if (result.isOk()) {
42
+ const person = await this.repository.read(result.value.id)
43
+
44
+ if (person) {
45
+ const jobs = new PersonEventJob()
46
+ jobs.addEvent(new InactivePersonEvent())
47
+
48
+ EventQueue.dispatchEventsForAggregate(jobs._id)
49
+ }
50
+
51
+ console.log('Person created with id:', result.value.id)
52
+ } else {
53
+ console.error('Error creating person:', result.value)
54
+ }
55
+
56
+ return ok(null)
57
+ }
58
+ }
@@ -0,0 +1,24 @@
1
+ import { createUnitTestApp } from '@/test/create-unit-test-app'
2
+ import { createPersonRequestMockup } from '@/test/mockup/person/create-person-request.mockup'
3
+ import { CreatePersonHandler } from '../create/create-person.handler'
4
+ import { DeletePersonHandler } from './delete-person.handler'
5
+
6
+ describe('DeletePersonHandler', () => {
7
+ const app = createUnitTestApp()
8
+
9
+ it('should delete a person', async () => {
10
+ const createResult = await app
11
+ .get(CreatePersonHandler)
12
+ .handle(createPersonRequestMockup)
13
+
14
+ expect(createResult.isOk()).toBeTruthy()
15
+
16
+ if (createResult.isOk()) {
17
+ const result = await app
18
+ .get(DeletePersonHandler)
19
+ .handle(createResult.value.id)
20
+
21
+ expect(result.isOk()).toBeTruthy()
22
+ }
23
+ })
24
+ })
@@ -0,0 +1,33 @@
1
+ import { IPersonRepository } from '@/domain/repositories/iperson.repository'
2
+ import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
3
+ import { RequestHandlerBase } from '@koalarx/nest/core/request-overflow/request-handler.base'
4
+ import {
5
+ failure,
6
+ ok,
7
+ RequestResult,
8
+ } from '@koalarx/nest/core/request-overflow/request-result'
9
+ import { Injectable } from '@nestjs/common'
10
+
11
+ @Injectable()
12
+ export class DeletePersonHandler extends RequestHandlerBase<
13
+ number,
14
+ RequestResult<ResourceNotFoundError, null>
15
+ > {
16
+ constructor(private readonly repository: IPersonRepository) {
17
+ super()
18
+ }
19
+
20
+ async handle(
21
+ id: number,
22
+ ): Promise<RequestResult<ResourceNotFoundError, null>> {
23
+ const person = await this.repository.read(id)
24
+
25
+ if (!person) {
26
+ return failure(new ResourceNotFoundError('Pessoa'))
27
+ }
28
+
29
+ await this.repository.delete(id)
30
+
31
+ return ok(null)
32
+ }
33
+ }
@@ -0,0 +1,49 @@
1
+ import { ReadManyPersonDto } from '@/domain/dtos/read-many-person.dto'
2
+ import {
3
+ CronJobHandlerBase,
4
+ CronJobResponse,
5
+ CronJobSettings,
6
+ } from '@koalarx/nest/core/backgroud-services/cron-service/cron-job.handler.base'
7
+ import { ok } from '@koalarx/nest/core/request-overflow/request-result'
8
+ import { ILoggingService } from '@koalarx/nest/services/logging/ilogging.service'
9
+ import { IRedLockService } from '@koalarx/nest/services/redlock/ired-lock.service'
10
+ import { Injectable } from '@nestjs/common'
11
+ import { DeletePersonHandler } from '../delete/delete-person.handler'
12
+ import { ReadManyPersonHandler } from '../read-many/read-many-person.handler'
13
+
14
+ @Injectable()
15
+ export class DeleteInactiveJob extends CronJobHandlerBase {
16
+ constructor(
17
+ redlockService: IRedLockService,
18
+ loggingService: ILoggingService,
19
+ private readonly readManyPerson: ReadManyPersonHandler,
20
+ private readonly deletePerson: DeletePersonHandler,
21
+ ) {
22
+ super(redlockService, loggingService)
23
+ }
24
+
25
+ protected async settings(): Promise<CronJobSettings> {
26
+ return {
27
+ isActive: true,
28
+ timeInMinutes: 1,
29
+ }
30
+ }
31
+
32
+ protected async run(): Promise<CronJobResponse> {
33
+ const result = await this.readManyPerson.handle(
34
+ new ReadManyPersonDto({ active: false }),
35
+ )
36
+
37
+ if (result.isOk()) {
38
+ for (const person of result.value.items) {
39
+ await this.deletePerson.handle(person.id)
40
+
41
+ console.log('Person with id was deleted:', person.id)
42
+ }
43
+ } else {
44
+ console.error('Error to search inactive people:', result.value)
45
+ }
46
+
47
+ return ok(null)
48
+ }
49
+ }
@@ -0,0 +1,3 @@
1
+ import { EventClass } from '@koalarx/nest/core/backgroud-services/event-service/event-class'
2
+
3
+ export class InactivePersonEvent extends EventClass {}
@@ -0,0 +1,27 @@
1
+ import { ReadManyPersonDto } from '@/domain/dtos/read-many-person.dto'
2
+ import { IPersonRepository } from '@/domain/repositories/iperson.repository'
3
+ import { EventHandlerBase } from '@koalarx/nest/core/backgroud-services/event-service/event-handler.base'
4
+ import { Injectable } from '@nestjs/common'
5
+ import { InactivePersonEvent } from './inactive-person-event'
6
+
7
+ @Injectable()
8
+ export class InactivePersonHandler extends EventHandlerBase {
9
+ constructor(private readonly repository: IPersonRepository) {
10
+ super(InactivePersonEvent)
11
+ }
12
+
13
+ async handleEvent(): Promise<void> {
14
+ const result = await this.repository.readMany(
15
+ new ReadManyPersonDto({ active: true }),
16
+ )
17
+
18
+ for (const person of result.items) {
19
+ person.active = false
20
+ await this.repository.save(person)
21
+ }
22
+
23
+ console.log(
24
+ 'InactivePersonHandler: Registros ativos inativados com sucesso!',
25
+ )
26
+ }
27
+ }
@@ -0,0 +1,11 @@
1
+ import { Person } from '@/domain/entities/person/person'
2
+ import { EventHandlerBase } from '@koalarx/nest/core/backgroud-services/event-service/event-handler.base'
3
+ import { EventJob } from '@koalarx/nest/core/backgroud-services/event-service/event-job'
4
+ import { Type } from '@nestjs/common'
5
+ import { InactivePersonHandler } from './inactive-person/inactive-person-handler'
6
+
7
+ export class PersonEventJob extends EventJob<Person> {
8
+ defineHandlers(): Type<EventHandlerBase>[] {
9
+ return [InactivePersonHandler]
10
+ }
11
+ }
@@ -0,0 +1,28 @@
1
+ import { createUnitTestApp } from '@/test/create-unit-test-app'
2
+ import { createPersonRequestMockup } from '@/test/mockup/person/create-person-request.mockup'
3
+ import { CreatePersonHandler } from '../create/create-person.handler'
4
+ import { ReadPersonHandler } from './read-person.handler'
5
+
6
+ describe('ReadPersonHandler', () => {
7
+ const app = createUnitTestApp()
8
+
9
+ it('should get a person by id', async () => {
10
+ const createResult = await app
11
+ .get(CreatePersonHandler)
12
+ .handle(createPersonRequestMockup)
13
+
14
+ expect(createResult.isOk()).toBeTruthy()
15
+
16
+ if (createResult.isOk()) {
17
+ const result = await app
18
+ .get(ReadPersonHandler)
19
+ .handle(createResult.value.id)
20
+
21
+ expect(result.isOk()).toBeTruthy()
22
+
23
+ if (result.isOk()) {
24
+ expect(result.value.id).toBe(createResult.value.id)
25
+ }
26
+ }
27
+ })
28
+ })
@@ -0,0 +1,37 @@
1
+ import { Person } from '@/domain/entities/person/person'
2
+ import { IPersonRepository } from '@/domain/repositories/iperson.repository'
3
+ import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
4
+ import { AutoMappingService } from '@koalarx/nest/core/mapping/auto-mapping.service'
5
+ import { RequestHandlerBase } from '@koalarx/nest/core/request-overflow/request-handler.base'
6
+ import {
7
+ failure,
8
+ ok,
9
+ RequestResult,
10
+ } from '@koalarx/nest/core/request-overflow/request-result'
11
+ import { Injectable } from '@nestjs/common'
12
+ import { ReadPersonResponse } from './read-person.response'
13
+
14
+ @Injectable()
15
+ export class ReadPersonHandler extends RequestHandlerBase<
16
+ number,
17
+ RequestResult<ResourceNotFoundError, ReadPersonResponse>
18
+ > {
19
+ constructor(
20
+ private readonly mapper: AutoMappingService,
21
+ private readonly repository: IPersonRepository,
22
+ ) {
23
+ super()
24
+ }
25
+
26
+ async handle(
27
+ id: number,
28
+ ): Promise<RequestResult<ResourceNotFoundError, ReadPersonResponse>> {
29
+ const person = await this.repository.read(id)
30
+
31
+ if (!person) {
32
+ return failure(new ResourceNotFoundError('Pessoa'))
33
+ }
34
+
35
+ return ok(this.mapper.map(person, Person, ReadPersonResponse))
36
+ }
37
+ }
@@ -0,0 +1,44 @@
1
+ import { AutoMap } from '@koalarx/nest/core/mapping/auto-mapping.decorator'
2
+ import { ApiProperty } from '@nestjs/swagger'
3
+
4
+ export class ReadPersonAddressResponse {
5
+ @ApiProperty()
6
+ @AutoMap()
7
+ id: number
8
+
9
+ @ApiProperty({ example: 'Street 1' })
10
+ @AutoMap()
11
+ address: string
12
+ }
13
+
14
+ export class ReadPersonPhoneResponse {
15
+ @ApiProperty()
16
+ @AutoMap()
17
+ id: number
18
+
19
+ @ApiProperty({ example: '22999999999' })
20
+ @AutoMap()
21
+ phone: string
22
+ }
23
+
24
+ export class ReadPersonResponse {
25
+ @ApiProperty()
26
+ @AutoMap()
27
+ id: number
28
+
29
+ @ApiProperty({ example: 'John Doe' })
30
+ @AutoMap()
31
+ name: string
32
+
33
+ @ApiProperty({ type: [ReadPersonPhoneResponse] })
34
+ @AutoMap({ type: () => ReadPersonPhoneResponse, isArray: true })
35
+ phones: Array<ReadPersonPhoneResponse>
36
+
37
+ @ApiProperty({ type: ReadPersonAddressResponse })
38
+ @AutoMap({ type: () => ReadPersonAddressResponse })
39
+ address: ReadPersonAddressResponse
40
+
41
+ @ApiProperty()
42
+ @AutoMap()
43
+ active: boolean
44
+ }
@@ -0,0 +1,67 @@
1
+ import { createUnitTestApp } from '@/test/create-unit-test-app'
2
+ import { createPersonRequestMockup } from '@/test/mockup/person/create-person-request.mockup'
3
+ import { RequestResult } from '@koalarx/nest/core/request-overflow/request-result'
4
+ import { CreatePersonHandler } from '../create/create-person.handler'
5
+ import { CreatePersonResponse } from '../create/create-person.response'
6
+ import { ReadManyPersonHandler } from './read-many-person.handler'
7
+ import { ReadManyPersonRequest } from './read-many-person.request'
8
+
9
+ describe('ReadManyPersonHandler', () => {
10
+ const app = createUnitTestApp()
11
+ const person = createPersonRequestMockup
12
+
13
+ let personId: number
14
+ let createResult: RequestResult<Error, CreatePersonResponse>
15
+
16
+ beforeAll(async () => {
17
+ createResult = await app
18
+ .get(CreatePersonHandler)
19
+ .handle(createPersonRequestMockup)
20
+
21
+ expect(createResult.isOk()).toBeTruthy()
22
+
23
+ if (createResult.isOk()) {
24
+ personId = createResult.value.id
25
+ }
26
+ })
27
+
28
+ it('should get all persons', async () => {
29
+ const result = await app
30
+ .get(ReadManyPersonHandler)
31
+ .handle(new ReadManyPersonRequest())
32
+
33
+ expect(result.isOk()).toBeTruthy()
34
+
35
+ if (result.isOk()) {
36
+ expect(result.value.items).toHaveLength(1)
37
+ }
38
+ })
39
+
40
+ it('should get persons by name', async () => {
41
+ const result = await app.get(ReadManyPersonHandler).handle(
42
+ new ReadManyPersonRequest({
43
+ name: person.name,
44
+ }),
45
+ )
46
+
47
+ expect(result.isOk()).toBeTruthy()
48
+
49
+ if (result.isOk()) {
50
+ expect(result.value.items[0].name).toBe(person.name)
51
+ expect(result.value.items).toHaveLength(1)
52
+ }
53
+ })
54
+
55
+ it('should get persons by status', async () => {
56
+ const result = await app.get(ReadManyPersonHandler).handle(
57
+ new ReadManyPersonRequest({
58
+ active: true,
59
+ }),
60
+ )
61
+
62
+ expect(result.value).toEqual({
63
+ items: [],
64
+ count: 0,
65
+ })
66
+ })
67
+ })
@@ -0,0 +1,47 @@
1
+ import { ReadManyPersonDto } from '@/domain/dtos/read-many-person.dto'
2
+ import { Person } from '@/domain/entities/person/person'
3
+ import { IPersonRepository } from '@/domain/repositories/iperson.repository'
4
+ import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
5
+ import { AutoMappingService } from '@koalarx/nest/core/mapping/auto-mapping.service'
6
+ import { RequestHandlerBase } from '@koalarx/nest/core/request-overflow/request-handler.base'
7
+ import {
8
+ ok,
9
+ RequestResult,
10
+ } from '@koalarx/nest/core/request-overflow/request-result'
11
+ import { Injectable } from '@nestjs/common'
12
+ import { ReadPersonResponse } from '../read/read-person.response'
13
+ import { ReadManyPersonRequest } from './read-many-person.request'
14
+ import { ReadManyPersonResponse } from './read-many-person.response'
15
+ import { ReadManyPersonValidator } from './read-many.validator'
16
+
17
+ @Injectable()
18
+ export class ReadManyPersonHandler extends RequestHandlerBase<
19
+ ReadManyPersonRequest,
20
+ RequestResult<ResourceNotFoundError, ReadManyPersonResponse>
21
+ > {
22
+ constructor(
23
+ private readonly mapper: AutoMappingService,
24
+ private readonly repository: IPersonRepository,
25
+ ) {
26
+ super()
27
+ }
28
+
29
+ async handle(
30
+ query: ReadManyPersonRequest,
31
+ ): Promise<RequestResult<ResourceNotFoundError, ReadManyPersonResponse>> {
32
+ const listOfPerson = await this.repository.readMany(
33
+ this.mapper.map(
34
+ new ReadManyPersonValidator(query).validate(),
35
+ ReadManyPersonRequest,
36
+ ReadManyPersonDto,
37
+ ),
38
+ )
39
+
40
+ return ok({
41
+ ...listOfPerson,
42
+ items: listOfPerson.items.map((person) =>
43
+ this.mapper.map(person, Person, ReadPersonResponse),
44
+ ),
45
+ })
46
+ }
47
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ PaginatedRequestProps,
3
+ PaginationRequest,
4
+ } from '@koalarx/nest/core/controllers/pagination.request'
5
+ import { AutoMap } from '@koalarx/nest/core/mapping/auto-mapping.decorator'
6
+ import { ApiProperty } from '@nestjs/swagger'
7
+
8
+ export class ReadManyPersonRequest extends PaginationRequest {
9
+ @ApiProperty({ required: false })
10
+ @AutoMap()
11
+ name?: string
12
+
13
+ @ApiProperty({ required: false })
14
+ @AutoMap()
15
+ active?: boolean
16
+
17
+ constructor(props?: PaginatedRequestProps<ReadManyPersonRequest>) {
18
+ super()
19
+ Object.assign(this, props)
20
+ }
21
+ }
@@ -0,0 +1,16 @@
1
+ import { ListResponse } from '@koalarx/nest/core'
2
+ import { AutoMap } from '@koalarx/nest/core/mapping/auto-mapping.decorator'
3
+ import { ApiProperty } from '@nestjs/swagger'
4
+ import { ReadPersonResponse } from '../read/read-person.response'
5
+
6
+ export class ReadManyPersonResponse
7
+ implements ListResponse<ReadPersonResponse>
8
+ {
9
+ @ApiProperty({ type: [ReadPersonResponse] })
10
+ @AutoMap()
11
+ items: ReadPersonResponse[]
12
+
13
+ @ApiProperty()
14
+ @AutoMap()
15
+ count: number
16
+ }
@@ -0,0 +1,16 @@
1
+ import { booleanSchema } from '@koalarx/nest/core/controllers/schemas/boolean.schema'
2
+ import { LIST_QUERY_SCHEMA } from '@koalarx/nest/core/controllers/schemas/list-query.schema'
3
+ import { RequestValidatorBase } from '@koalarx/nest/core/request-overflow/request-validator.base'
4
+ import { z, ZodType, ZodTypeDef } from 'zod'
5
+ import { ReadManyPersonRequest } from './read-many-person.request'
6
+
7
+ export class ReadManyPersonValidator extends RequestValidatorBase<ReadManyPersonRequest> {
8
+ protected get schema(): ZodType<any, ZodTypeDef, any> {
9
+ return LIST_QUERY_SCHEMA.merge(
10
+ z.object({
11
+ name: z.string().optional().nullable(),
12
+ active: booleanSchema().optional().nullable(),
13
+ }),
14
+ )
15
+ }
16
+ }
@@ -0,0 +1,47 @@
1
+ import { createUnitTestApp } from '@/test/create-unit-test-app'
2
+ import { createPersonRequestMockup } from '@/test/mockup/person/create-person-request.mockup'
3
+ import { CreatePersonHandler } from '../create/create-person.handler'
4
+ import { ReadPersonHandler } from '../read/read-person.handler'
5
+ import { UpdatePersonHandler } from './update-person.handler'
6
+
7
+ describe('UpdatePersonHandler', () => {
8
+ const app = createUnitTestApp()
9
+
10
+ it('should update a person by id', async () => {
11
+ const person = createPersonRequestMockup
12
+ const createResult = await app
13
+ .get(CreatePersonHandler)
14
+ .handle(createPersonRequestMockup)
15
+
16
+ expect(createResult.isOk()).toBeTruthy()
17
+
18
+ if (createResult.isOk()) {
19
+ const updateResult = await app.get(UpdatePersonHandler).handle({
20
+ id: createResult.value.id,
21
+ data: {
22
+ ...person,
23
+ address: {
24
+ id: 1,
25
+ ...person.address,
26
+ },
27
+ active: true,
28
+ },
29
+ })
30
+
31
+ expect(updateResult.isOk()).toBeTruthy()
32
+
33
+ const result = await app
34
+ .get(ReadPersonHandler)
35
+ .handle(createResult.value.id)
36
+
37
+ expect(result.isOk()).toBeTruthy()
38
+
39
+ if (result.isOk()) {
40
+ expect(result.value.name).toEqual(person.name)
41
+ expect(result.value.active).toEqual(true)
42
+ expect(result.value.address.address).toEqual(person.address.address)
43
+ expect(result.value.phones[0].phone).toEqual(person.phones[0].phone)
44
+ }
45
+ }
46
+ })
47
+ })
@@ -0,0 +1,57 @@
1
+ import { Person } from '@/domain/entities/person/person'
2
+ import { IPersonRepository } from '@/domain/repositories/iperson.repository'
3
+ import { ResourceNotFoundError } from '@koalarx/nest/core/errors/resource-not-found.error'
4
+ import { AutoMappingService } from '@koalarx/nest/core/mapping/auto-mapping.service'
5
+ import { RequestHandlerBase } from '@koalarx/nest/core/request-overflow/request-handler.base'
6
+ import {
7
+ failure,
8
+ ok,
9
+ RequestResult,
10
+ } from '@koalarx/nest/core/request-overflow/request-result'
11
+ import { Injectable } from '@nestjs/common'
12
+ import { UpdatePersonRequest } from './update-person.request'
13
+ import { UpdatePersonValidator } from './update-person.validator'
14
+
15
+ type UpdatePersonHandleRequest = {
16
+ id: number
17
+ data: UpdatePersonRequest
18
+ }
19
+
20
+ @Injectable()
21
+ export class UpdatePersonHandler extends RequestHandlerBase<
22
+ UpdatePersonHandleRequest,
23
+ RequestResult<ResourceNotFoundError, null>
24
+ > {
25
+ constructor(
26
+ private readonly mapper: AutoMappingService,
27
+ private readonly repository: IPersonRepository,
28
+ ) {
29
+ super()
30
+ }
31
+
32
+ async handle({
33
+ id,
34
+ data,
35
+ }: UpdatePersonHandleRequest): Promise<RequestResult<Error, null>> {
36
+ const personInBd = await this.repository.read(id)
37
+
38
+ if (!personInBd) {
39
+ return failure(new ResourceNotFoundError('Person'))
40
+ }
41
+
42
+ const person = this.mapper.map(
43
+ new UpdatePersonValidator(data).validate(),
44
+ UpdatePersonRequest,
45
+ Person,
46
+ )
47
+
48
+ personInBd.name = person.name
49
+ personInBd.active = person.active
50
+ personInBd.address.address = person.address.address
51
+ personInBd.phones.update(person.phones.toArray())
52
+
53
+ await this.repository.save(personInBd)
54
+
55
+ return ok(null)
56
+ }
57
+ }
@@ -0,0 +1,36 @@
1
+ import { AutoMap } from '@koalarx/nest/core/mapping/auto-mapping.decorator'
2
+ import { ApiProperty } from '@nestjs/swagger'
3
+ import {
4
+ PersistPersonAddressRequest,
5
+ PersistPersonPhoneRequest,
6
+ } from '../common/persist-person.request'
7
+
8
+ export class UpdatePersonAddressRequest extends PersistPersonAddressRequest {
9
+ @ApiProperty()
10
+ @AutoMap()
11
+ id: number
12
+ }
13
+
14
+ export class UpdatePersonPhoneRequest extends PersistPersonPhoneRequest {
15
+ @ApiProperty({ required: false })
16
+ @AutoMap()
17
+ id?: number
18
+ }
19
+
20
+ export class UpdatePersonRequest {
21
+ @ApiProperty({ example: 'Mark Willians' })
22
+ @AutoMap()
23
+ name: string
24
+
25
+ @ApiProperty({ example: false })
26
+ @AutoMap()
27
+ active: boolean
28
+
29
+ @ApiProperty({ type: [UpdatePersonPhoneRequest] })
30
+ @AutoMap({ type: () => UpdatePersonPhoneRequest, isArray: true })
31
+ phones: Array<UpdatePersonPhoneRequest>
32
+
33
+ @ApiProperty({ type: UpdatePersonAddressRequest })
34
+ @AutoMap({ type: () => UpdatePersonAddressRequest })
35
+ address: UpdatePersonAddressRequest
36
+ }
@@ -0,0 +1,21 @@
1
+ import { RequestValidatorBase } from '@koalarx/nest/core/request-overflow/request-validator.base'
2
+ import { z, ZodType, ZodTypeDef } from 'zod'
3
+ import { UpdatePersonRequest } from './update-person.request'
4
+
5
+ export class UpdatePersonValidator extends RequestValidatorBase<UpdatePersonRequest> {
6
+ protected get schema(): ZodType<any, ZodTypeDef, any> {
7
+ return z.object({
8
+ name: z.string(),
9
+ phones: z.array(
10
+ z.object({
11
+ id: z.number().nullable().optional(),
12
+ phone: z.string(),
13
+ }),
14
+ ),
15
+ address: z.object({
16
+ id: z.number(),
17
+ address: z.string(),
18
+ }),
19
+ })
20
+ }
21
+ }
@@ -0,0 +1,6 @@
1
+ import { envSchema } from '@koalarx/nest/env/env'
2
+ import { z } from 'zod'
3
+
4
+ export const env = envSchema.merge(z.object({}))
5
+
6
+ export type EnvType = z.infer<typeof env>