From 5b2a7c4370ccfe540c4f47061d28f814ff911487 Mon Sep 17 00:00:00 2001 From: Maximilian Kreuzkam Date: Fri, 24 Nov 2023 16:21:22 +0100 Subject: [PATCH] Adjust controllers to use mappings. --- .../api/organisation.controller.spec.ts | 81 +++++++++++-------- .../api/organisation.controller.ts | 25 +++--- .../organisation/api/organisation.uc.spec.ts | 38 ++++++--- .../organisation/api/organisation.uc.ts | 14 +++- .../organisation/organisation-api.module.ts | 3 +- 5 files changed, 102 insertions(+), 59 deletions(-) diff --git a/src/modules/organisation/api/organisation.controller.spec.ts b/src/modules/organisation/api/organisation.controller.spec.ts index 27c007383..52ceb7056 100644 --- a/src/modules/organisation/api/organisation.controller.spec.ts +++ b/src/modules/organisation/api/organisation.controller.spec.ts @@ -10,11 +10,13 @@ import { faker } from '@faker-js/faker'; import { CreatedOrganisationDto } from './created-organisation.dto.js'; import { OrganisationByIdParams } from './organisation-by-id.params.js'; import { OrganisationResponse } from './organisation.response.js'; -import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; import { HttpException } from '@nestjs/common'; import { FindOrganisationQueryParams } from './find-organisation-query.param.js'; import { Paged } from '../../../shared/paging/paged.js'; import { FindOrganisationDto } from './find-organisation.dto.js'; +import { SchulConnexError } from '../../../shared/error/schul-connex.error.js'; +import { plainToClass } from 'class-transformer'; +import { ErrorModule } from '../../../shared/error/error.module.js'; describe('OrganisationController', () => { let module: TestingModule; @@ -23,7 +25,7 @@ describe('OrganisationController', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MapperTestModule], + imports: [MapperTestModule, ErrorModule], providers: [ OrganisationController, OrganisationApiMapperProfile, @@ -50,26 +52,28 @@ describe('OrganisationController', () => { }); describe('createOrganisation', () => { - it('should not throw an error', async () => { - const params: CreateOrganisationBodyParams = { - kennung: faker.lorem.word(), - name: faker.lorem.word(), - namensergaenzung: faker.lorem.word(), - kuerzel: faker.lorem.word(), - typ: OrganisationsTyp.SONSTIGE, - }; - - const returnedValue: CreatedOrganisationDto = { - id: faker.string.uuid(), - kennung: faker.lorem.word(), - name: faker.lorem.word(), - namensergaenzung: faker.lorem.word(), - kuerzel: faker.lorem.word(), - typ: OrganisationsTyp.SONSTIGE, - }; - organisationUcMock.createOrganisation.mockResolvedValue(returnedValue); - await expect(organisationController.createOrganisation(params)).resolves.not.toThrow(); - expect(organisationUcMock.createOrganisation).toHaveBeenCalledTimes(1); + describe('when usecase returns a DTO', () => { + it('should not throw an error', async () => { + const params: CreateOrganisationBodyParams = { + kennung: faker.lorem.word(), + name: faker.lorem.word(), + namensergaenzung: faker.lorem.word(), + kuerzel: faker.lorem.word(), + typ: OrganisationsTyp.SONSTIGE, + }; + + const returnedValue: CreatedOrganisationDto = plainToClass(CreatedOrganisationDto, { + id: faker.string.uuid(), + kennung: faker.lorem.word(), + name: faker.lorem.word(), + namensergaenzung: faker.lorem.word(), + kuerzel: faker.lorem.word(), + typ: OrganisationsTyp.SONSTIGE, + }); + organisationUcMock.createOrganisation.mockResolvedValue(returnedValue); + await expect(organisationController.createOrganisation(params)).resolves.not.toThrow(); + expect(organisationUcMock.createOrganisation).toHaveBeenCalledTimes(1); + }); }); }); @@ -77,26 +81,37 @@ describe('OrganisationController', () => { const params: OrganisationByIdParams = { organisationId: faker.string.uuid(), }; - const response: OrganisationResponse = { + const response: OrganisationResponse = plainToClass(OrganisationResponse, { id: params.organisationId, kennung: faker.lorem.word(), name: faker.lorem.word(), namensergaenzung: faker.lorem.word(), kuerzel: faker.lorem.word(), typ: OrganisationsTyp.SONSTIGE, - }; + }); - it('should find an organization by it id', async () => { - organisationUcMock.findOrganisationById.mockResolvedValue(response); - await expect(organisationController.findOrganisationById(params)).resolves.not.toThrow(); - expect(organisationUcMock.findOrganisationById).toHaveBeenCalledTimes(1); + describe('when usecase returns an OrganisationResponse', () => { + it('should not throw', async () => { + organisationUcMock.findOrganisationById.mockResolvedValue(response); + await expect(organisationController.findOrganisationById(params)).resolves.not.toThrow(); + expect(organisationUcMock.findOrganisationById).toHaveBeenCalledTimes(1); + }); }); - it('should throw an error', async () => { - const mockError: EntityNotFoundError = new EntityNotFoundError('organization', faker.string.uuid()); - organisationUcMock.findOrganisationById.mockRejectedValue(mockError); - await expect(organisationController.findOrganisationById(params)).rejects.toThrowError(HttpException); - expect(organisationUcMock.findOrganisationById).toHaveBeenCalledTimes(1); + describe('when usecase returns a SchulConnexError', () => { + it('should return HttpException', async () => { + const mockError: SchulConnexError = new SchulConnexError({ + beschreibung: 'SchulConneX', + code: 500, + titel: 'SchulConneX Fehler', + subcode: '0', + }); + organisationUcMock.findOrganisationById.mockResolvedValue(mockError); + await expect(organisationController.findOrganisationById(params)).resolves.toBeInstanceOf( + HttpException, + ); + expect(organisationUcMock.findOrganisationById).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/src/modules/organisation/api/organisation.controller.ts b/src/modules/organisation/api/organisation.controller.ts index c40f7a173..af7f2301d 100644 --- a/src/modules/organisation/api/organisation.controller.ts +++ b/src/modules/organisation/api/organisation.controller.ts @@ -1,6 +1,6 @@ import { Mapper } from '@automapper/core'; import { getMapperToken } from '@automapper/nestjs'; -import { Body, Controller, Get, HttpException, HttpStatus, Inject, Param, Post, Query } from '@nestjs/common'; +import { Body, Controller, Get, HttpException, Inject, Param, Post, Query } from '@nestjs/common'; import { OrganisationUc } from './organisation.uc.js'; import { ApiBadRequestResponse, @@ -22,6 +22,7 @@ import { FindOrganisationDto } from './find-organisation.dto.js'; import { PagedResponse } from '../../../shared/paging/paged.response.js'; import { Paged, PagingHeadersObject } from '../../../shared/paging/index.js'; import { FindOrganisationQueryParams } from './find-organisation-query.param.js'; +import { SchulConnexError } from '../../../shared/error/schul-connex.error.js'; @ApiTags('organisationen') @Controller({ path: 'organisationen' }) @@ -38,14 +39,19 @@ export class OrganisationController { @ApiUnauthorizedResponse({ description: 'Not authorized to create the organisation.' }) @ApiForbiddenResponse({ description: 'Not permitted to create the organisation.' }) @ApiInternalServerErrorResponse({ description: 'Internal server error while creating the organisation.' }) - public async createOrganisation(@Body() params: CreateOrganisationBodyParams): Promise { + public async createOrganisation( + @Body() params: CreateOrganisationBodyParams, + ): Promise { const organisationDto: CreateOrganisationDto = this.mapper.map( params, CreateOrganisationBodyParams, CreateOrganisationDto, ); - const createdOrganisation: CreatedOrganisationDto = await this.uc.createOrganisation(organisationDto); - return this.mapper.map(createdOrganisation, CreatedOrganisationDto, OrganisationResponse); + const result: CreatedOrganisationDto | SchulConnexError = await this.uc.createOrganisation(organisationDto); + if (result instanceof CreatedOrganisationDto) { + return this.mapper.map(result, CreatedOrganisationDto, OrganisationResponse); + } + return this.mapper.map(result, SchulConnexError, HttpException); } @Get(':organisationId') @@ -58,12 +64,13 @@ export class OrganisationController { public async findOrganisationById( @Param() params: OrganisationByIdParams, ): Promise { - try { - const organisation: OrganisationResponse = await this.uc.findOrganisationById(params.organisationId); - return organisation; - } catch (error) { - throw new HttpException('Requested Entity does not exist', HttpStatus.NOT_FOUND); + const result: OrganisationResponse | SchulConnexError = await this.uc.findOrganisationById( + params.organisationId, + ); + if (result instanceof OrganisationResponse) { + return result; } + return this.mapper.map(result, SchulConnexError, HttpException); } @Get() diff --git a/src/modules/organisation/api/organisation.uc.spec.ts b/src/modules/organisation/api/organisation.uc.spec.ts index 2e381d3d4..cfd7144f2 100644 --- a/src/modules/organisation/api/organisation.uc.spec.ts +++ b/src/modules/organisation/api/organisation.uc.spec.ts @@ -12,6 +12,9 @@ import { OrganisationsTyp } from '../domain/organisation.enum.js'; import { FindOrganisationDto } from './find-organisation.dto.js'; import { OrganisationResponse } from './organisation.response.js'; import { Paged } from '../../../shared/paging/paged.js'; +import { SchulConnexError } from '../../../shared/error/schul-connex.error.js'; +import { CreatedOrganisationDto } from './created-organisation.dto.js'; +import { DomainToSchulConnexErrorMapper } from '../../../shared/error/domain-to-schulconnex-error.mapper.js'; describe('OrganisationUc', () => { let module: TestingModule; @@ -24,6 +27,7 @@ describe('OrganisationUc', () => { providers: [ OrganisationUc, OrganisationApiMapperProfile, + DomainToSchulConnexErrorMapper, { provide: OrganisationService, useValue: createMock(), @@ -47,22 +51,32 @@ describe('OrganisationUc', () => { }); describe('createOrganisation', () => { - it('should create an organisation', async () => { - organisationServiceMock.createOrganisation.mockResolvedValue({ - ok: true, - value: DoFactory.createOrganisation(true), + describe('when result is ok', () => { + it('should create an organisation', async () => { + organisationServiceMock.createOrganisation.mockResolvedValue({ + ok: true, + value: DoFactory.createOrganisation(true), + }); + await expect(organisationUc.createOrganisation({} as CreateOrganisationDto)).resolves.toBeInstanceOf( + CreatedOrganisationDto, + ); }); - await expect(organisationUc.createOrganisation({} as CreateOrganisationDto)).resolves.not.toThrow(); }); - it('should throw an error', async () => { - organisationServiceMock.createOrganisation.mockResolvedValue({ - ok: false, - error: new EntityCouldNotBeCreated(''), + describe('when result is not ok', () => { + it('should return an error', async () => { + organisationServiceMock.createOrganisation.mockResolvedValue({ + ok: false, + error: new EntityCouldNotBeCreated(''), + }); + await expect(organisationUc.createOrganisation({} as CreateOrganisationDto)).resolves.toBeInstanceOf( + SchulConnexError, + ); + }); + + it('bla', () => { + expect(EntityCouldNotBeCreated.name).toEqual(new EntityCouldNotBeCreated('').constructor.name); }); - await expect(organisationUc.createOrganisation({} as CreateOrganisationDto)).rejects.toThrowError( - EntityCouldNotBeCreated, - ); }); }); diff --git a/src/modules/organisation/api/organisation.uc.ts b/src/modules/organisation/api/organisation.uc.ts index 7b3933d2f..a318664f8 100644 --- a/src/modules/organisation/api/organisation.uc.ts +++ b/src/modules/organisation/api/organisation.uc.ts @@ -8,6 +8,8 @@ import { CreatedOrganisationDto } from './created-organisation.dto.js'; import { OrganisationResponse } from './organisation.response.js'; import { Paged } from '../../../shared/paging/paged.js'; import { FindOrganisationDto } from './find-organisation.dto.js'; +import { SchulConnexError } from '../../../shared/error/schul-connex.error.js'; +import { EntityCouldNotBeCreated } from '../../../shared/error/entity-could-not-be-created.error.js'; @Injectable() export class OrganisationUc { @@ -16,25 +18,29 @@ export class OrganisationUc { @Inject(getMapperToken()) private readonly mapper: Mapper, ) {} - public async createOrganisation(organisationDto: CreateOrganisationDto): Promise { + public async createOrganisation( + organisationDto: CreateOrganisationDto, + ): Promise { const organisationDo: OrganisationDo = this.mapper.map( organisationDto, CreateOrganisationDto, OrganisationDo, ); const result: Result> = await this.organisationService.createOrganisation(organisationDo); + if (result.ok) { return this.mapper.map(result.value, OrganisationDo, CreatedOrganisationDto); } - throw result.error; + + return this.mapper.map(result.error, EntityCouldNotBeCreated, SchulConnexError); } - public async findOrganisationById(id: string): Promise { + public async findOrganisationById(id: string): Promise { const result: Result> = await this.organisationService.findOrganisationById(id); if (result.ok) { return this.mapper.map(result.value, OrganisationDo, OrganisationResponse); } - throw result.error; + return this.mapper.map(result.error, result.error.constructor.name, SchulConnexError); } public async findAll(organisationDto: FindOrganisationDto): Promise> { diff --git a/src/modules/organisation/organisation-api.module.ts b/src/modules/organisation/organisation-api.module.ts index af3bdbabf..12515c6eb 100644 --- a/src/modules/organisation/organisation-api.module.ts +++ b/src/modules/organisation/organisation-api.module.ts @@ -3,9 +3,10 @@ import { OrganisationController } from './api/organisation.controller.js'; import { OrganisationModule } from './organisation.module.js'; import { OrganisationUc } from './api/organisation.uc.js'; import { OrganisationApiMapperProfile } from './api/organisation-api.mapper.profile.js'; +import { ErrorModule } from '../../shared/error/error.module.js'; @Module({ - imports: [OrganisationModule], + imports: [OrganisationModule, ErrorModule], providers: [OrganisationApiMapperProfile, OrganisationUc], controllers: [OrganisationController], })