Skip to content

Commit

Permalink
Adjust controllers to use mappings.
Browse files Browse the repository at this point in the history
  • Loading branch information
mkreuzkam-cap committed Nov 24, 2023
1 parent 41242d7 commit 5b2a7c4
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 59 deletions.
81 changes: 48 additions & 33 deletions src/modules/organisation/api/organisation.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,7 +25,7 @@ describe('OrganisationController', () => {

beforeAll(async () => {
module = await Test.createTestingModule({
imports: [MapperTestModule],
imports: [MapperTestModule, ErrorModule],
providers: [
OrganisationController,
OrganisationApiMapperProfile,
Expand All @@ -50,53 +52,66 @@ 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);
});
});
});

describe('findOrganisationById', () => {
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);
});
});
});

Expand Down
25 changes: 16 additions & 9 deletions src/modules/organisation/api/organisation.controller.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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' })
Expand All @@ -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<OrganisationResponse> {
public async createOrganisation(
@Body() params: CreateOrganisationBodyParams,
): Promise<OrganisationResponse | HttpException> {
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')
Expand All @@ -58,12 +64,13 @@ export class OrganisationController {
public async findOrganisationById(
@Param() params: OrganisationByIdParams,
): Promise<OrganisationResponse | HttpException> {
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()
Expand Down
38 changes: 26 additions & 12 deletions src/modules/organisation/api/organisation.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,6 +27,7 @@ describe('OrganisationUc', () => {
providers: [
OrganisationUc,
OrganisationApiMapperProfile,
DomainToSchulConnexErrorMapper,
{
provide: OrganisationService,
useValue: createMock<OrganisationService>(),
Expand All @@ -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,
);
});
});

Expand Down
14 changes: 10 additions & 4 deletions src/modules/organisation/api/organisation.uc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -16,25 +18,29 @@ export class OrganisationUc {
@Inject(getMapperToken()) private readonly mapper: Mapper,
) {}

public async createOrganisation(organisationDto: CreateOrganisationDto): Promise<CreatedOrganisationDto> {
public async createOrganisation(
organisationDto: CreateOrganisationDto,
): Promise<CreatedOrganisationDto | SchulConnexError> {
const organisationDo: OrganisationDo<false> = this.mapper.map(
organisationDto,
CreateOrganisationDto,
OrganisationDo,
);
const result: Result<OrganisationDo<true>> = 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<OrganisationResponse> {
public async findOrganisationById(id: string): Promise<OrganisationResponse | SchulConnexError> {
const result: Result<OrganisationDo<true>> = 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<Paged<OrganisationResponse>> {
Expand Down
3 changes: 2 additions & 1 deletion src/modules/organisation/organisation-api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
})
Expand Down

0 comments on commit 5b2a7c4

Please sign in to comment.