Skip to content

Commit

Permalink
EW-525: Add GET /personenkontexte endpoint (#82)
Browse files Browse the repository at this point in the history
* Add endpoint GET /api/personenkontexte to get all Personenkontexte (supports filters and pagination)
* /api/person/{personId}/personenkontexte now also supports pagination
* Add PersonenkontextScope and use it in findAll for Personenkontexte
* Use SichtfreigabeType instead of boolean in personenkontext entity and DO (requires re-initializing the database)
  • Loading branch information
mkreuzkam-cap authored Nov 14, 2023
1 parent 13cddb8 commit b92837e
Show file tree
Hide file tree
Showing 31 changed files with 475 additions and 228 deletions.
3 changes: 1 addition & 2 deletions src/modules/person/api/created-personenkontext.dto.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { AutoMap } from '@automapper/classes';
import { Jahrgangsstufe, Personenstatus, Rolle } from '../domain/personenkontext.enums.js';
import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js';
import { CreatedPersonenkontextOrganisationDto } from './created-personenkontext-organisation.dto.js';
import { LoeschungDto } from './loeschung.dto.js';
import { SichtfreigabeType } from './personen-query.param.js';

export class CreatedPersonenkontextDto {
@AutoMap()
Expand Down
2 changes: 1 addition & 1 deletion src/modules/person/api/find-personendatensatz.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AutoMap } from '@automapper/classes';
import { PagedDto } from '../../../shared/paging/index.js';
import { SichtfreigabeType } from './personen-query.param.js';
import { SichtfreigabeType } from '../domain/personenkontext.enums.js';

export class FindPersonendatensatzDto extends PagedDto {
@AutoMap()
Expand Down
8 changes: 4 additions & 4 deletions src/modules/person/api/find-personenkontext.dto.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AutoMap } from '@automapper/classes';
import { Rolle, Personenstatus } from '../domain/personenkontext.enums.js';
import { SichtfreigabeType } from './personen-query.param.js';
import { PagedDto } from '../../../shared/paging/paged.dto.js';
import { Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js';

export class FindPersonenkontextDto {
export class FindPersonenkontextDto extends PagedDto {
@AutoMap()
public personId!: string;
public personId?: string;

@AutoMap()
public readonly referrer?: string;
Expand Down
26 changes: 10 additions & 16 deletions src/modules/person/api/person-api.mapper.profile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { faker } from '@faker-js/faker';
import { Test, TestingModule } from '@nestjs/testing';
import { DoFactory, MapperTestModule } from '../../../../test/utils/index.js';
import { MappingError } from '../../../shared/error/index.js';
import { CreatePersonDto } from './create-person.dto.js';
import { PersonDo } from '../domain/person.do.js';
import { PersonenkontextDo } from '../domain/personenkontext.do.js';
import { Jahrgangsstufe, Personenstatus, Rolle } from '../domain/personenkontext.enums.js';
import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js';
import { CreatePersonBodyParams } from './create-person.body.params.js';
import { CreatePersonDto } from './create-person.dto.js';
import { CreatePersonenkontextBodyParams } from './create-personenkontext.body.params.js';
import { CreatePersonenkontextDto } from './create-personenkontext.dto.js';
import { CreatedPersonenkontextDto } from './created-personenkontext.dto.js';
Expand All @@ -17,19 +17,19 @@ import { FindPersonenkontextByIdParams } from './find-personenkontext-by-id.para
import { FindPersonenkontextDto } from './find-personenkontext.dto.js';
import { LoeschungDto } from './loeschung.dto.js';
import { LoeschungResponse } from './loeschung.response.js';
import { PersonApiMapperProfile, personVisibilityToBooleanConverter } from './person-api.mapper.profile.js';
import { PersonApiMapperProfile } from './person-api.mapper.profile.js';
import { PersonBirthParams } from './person-birth.params.js';
import { PersonGeburtDto } from './person-geburt.dto.js';
import { PersonNameDto } from './person-name.dto.js';
import { PersonNameParams } from './person-name.params.js';
import { PersonDto } from './person.dto.js';
import { PersonResponse } from './person.response.js';
import { SichtfreigabeType } from './personen-query.param.js';
import { PersonendatensatzDto } from './personendatensatz.dto.js';
import { PersonendatensatzResponse } from './personendatensatz.response.js';
import { PersonenkontextQueryParams } from './personenkontext-query.params.js';
import { PersonenkontextDto } from './personenkontext.dto.js';
import { PersonenkontextResponse } from './personenkontext.response.js';
import { PersonenkontextdatensatzResponse } from './personenkontextdatensatz.response.js';

describe('PersonApiMapperProfile', () => {
let module: TestingModule;
Expand All @@ -51,18 +51,6 @@ describe('PersonApiMapperProfile', () => {
expect(sut).toBeDefined();
});

describe('personVisibilityToBooleanConverter', () => {
describe('when converting Visibility type to boolean', () => {
it('should convert VisibilityType.JA to true', () => {
expect(personVisibilityToBooleanConverter.convert(SichtfreigabeType.JA)).toBe(true);
});

it('should convert VisibilityType.NEIN to false', () => {
expect(personVisibilityToBooleanConverter.convert(SichtfreigabeType.NEIN)).toBe(false);
});
});
});

describe('when mapper is initialized', () => {
it('should map CreatePersonBodyParams to CreatePersonDTO', () => {
const params: CreatePersonBodyParams = {
Expand Down Expand Up @@ -205,5 +193,11 @@ describe('PersonApiMapperProfile', () => {
sut.map({} as PersonendatensatzDto, PersonendatensatzDto, PersonendatensatzResponse),
).not.toThrowError(MappingError);
});

it('should map PersonenkontextDto to PersonenkontextdatensatzResponse', () => {
expect(() =>
sut.map({} as PersonenkontextDto, PersonenkontextDto, PersonenkontextdatensatzResponse),
).not.toThrowError(MappingError);
});
});
});
59 changes: 23 additions & 36 deletions src/modules/person/api/person-api.mapper.profile.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
import {
Converter,
Mapper,
MappingProfile,
convertUsing,
createMap,
forMember,
ignore,
mapFrom,
} from '@automapper/core';
import { Mapper, MappingProfile, createMap, forMember, ignore, mapFrom } from '@automapper/core';
import { AutomapperProfile, getMapperToken } from '@automapper/nestjs';
import { Inject, Injectable } from '@nestjs/common';
import { UserDo } from '../../keycloak-administration/index.js';
import { OrganisationDo } from '../../organisation/domain/organisation.do.js';
import { CreatePersonDto } from './create-person.dto.js';
import { PersonDo } from '../domain/person.do.js';
import { PersonenkontextDo } from '../domain/personenkontext.do.js';
import { CreatePersonBodyParams } from './create-person.body.params.js';
import { CreatePersonDto } from './create-person.dto.js';
import { CreatePersonenkontextBodyParams } from './create-personenkontext.body.params.js';
import { CreatePersonenkontextDto } from './create-personenkontext.dto.js';
import { CreatedPersonenkontextOrganisationDto } from './created-personenkontext-organisation.dto.js';
Expand All @@ -28,28 +19,18 @@ import { LoeschungDto } from './loeschung.dto.js';
import { LoeschungResponse } from './loeschung.response.js';
import { PersonBirthParams } from './person-birth.params.js';
import { PersonGeburtDto } from './person-geburt.dto.js';
import { PersonIdResponse } from './person-id.response.js';
import { PersonNameDto } from './person-name.dto.js';
import { PersonNameParams } from './person-name.params.js';
import { PersonDto } from './person.dto.js';
import { PersonResponse } from './person.response.js';
import { PersonenQueryParams, SichtfreigabeType } from './personen-query.param.js';
import { PersonenQueryParams } from './personen-query.param.js';
import { PersonendatensatzDto } from './personendatensatz.dto.js';
import { PersonendatensatzResponse } from './personendatensatz.response.js';
import { PersonenkontextQueryParams } from './personenkontext-query.params.js';
import { PersonenkontextDto } from './personenkontext.dto.js';
import { PersonenkontextResponse } from './personenkontext.response.js';

export const personVisibilityToBooleanConverter: Converter<SichtfreigabeType, boolean> = {
convert(source: SichtfreigabeType) {
switch (source) {
case SichtfreigabeType.JA:
return true;
case SichtfreigabeType.NEIN:
default:
return false;
}
},
};
import { PersonenkontextdatensatzResponse } from './personenkontextdatensatz.response.js';

@Injectable()
export class PersonApiMapperProfile extends AutomapperProfile {
Expand Down Expand Up @@ -241,14 +222,15 @@ export class PersonApiMapperProfile extends AutomapperProfile {
forMember((dest: PersonenkontextDo<boolean>) => dest.organisation, ignore()),
forMember((dest: PersonenkontextDo<boolean>) => dest.loeschungZeitpunkt, ignore()),
forMember((dest: PersonenkontextDo<boolean>) => dest.revision, ignore()),
forMember((dest: PersonenkontextDo<boolean>) => dest.sichtfreigabe, ignore()),
);

createMap(
mapper,
PersonenkontextDo,
CreatedPersonenkontextDto,
forMember(
(dest: PersonenkontextDto) => dest.loeschung,
(dest: CreatedPersonenkontextDto) => dest.loeschung,
mapFrom((src: PersonenkontextDo<boolean>) =>
src.loeschungZeitpunkt ? new LoeschungDto({ zeitpunkt: src.loeschungZeitpunkt }) : undefined,
),
Expand Down Expand Up @@ -280,17 +262,6 @@ export class PersonApiMapperProfile extends AutomapperProfile {
forMember((dest: PersonenkontextDo<boolean>) => dest.id, ignore()),
forMember((dest: PersonenkontextDo<boolean>) => dest.createdAt, ignore()),
forMember((dest: PersonenkontextDo<boolean>) => dest.updatedAt, ignore()),
forMember(
(dest: PersonenkontextDo<boolean>) => dest.id,
mapFrom((src: FindPersonenkontextDto) => src.personId),
),
forMember(
(dest: PersonenkontextDo<boolean>) => dest.sichtfreigabe,
convertUsing(
personVisibilityToBooleanConverter,
(src: FindPersonenkontextDto) => src.sichtfreigabe,
),
),
);

createMap(mapper, FindPersonenkontextByIdParams, FindPersonenkontextByIdDto);
Expand Down Expand Up @@ -365,6 +336,22 @@ export class PersonApiMapperProfile extends AutomapperProfile {
createMap(mapper, PersonGeburtDto, PersonBirthParams);

createMap(mapper, PersonendatensatzDto, PersonendatensatzResponse);

createMap(
mapper,
PersonenkontextDto,
PersonenkontextdatensatzResponse,
forMember(
(dest: PersonenkontextdatensatzResponse) => dest.person,
mapFrom((src: PersonenkontextDto) => new PersonIdResponse({ id: src.personId })),
),
forMember(
(dest: PersonenkontextdatensatzResponse) => dest.personenkontexte,
mapFrom((src: PersonenkontextDto) => [
mapper.map(src, PersonenkontextDto, PersonenkontextResponse),
]),
),
);
};
}
}
12 changes: 12 additions & 0 deletions src/modules/person/api/person-id.response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { AutoMap } from '@automapper/classes';
import { ApiProperty } from '@nestjs/swagger';

export class PersonIdResponse {
@AutoMap()
@ApiProperty()
public id!: string;

public constructor(props: Readonly<PersonIdResponse>) {
Object.assign(this, props);
}
}
40 changes: 23 additions & 17 deletions src/modules/person/api/person.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import { faker } from '@faker-js/faker';
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { HttpException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { MapperTestModule } from '../../../../test/utils/index.js';
import { Paged, PagedResponse } from '../../../shared/paging/index.js';
import { Geschlecht, Vertrauensstufe } from '../domain/person.enums.js';
import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js';
import { CreatePersonBodyParams } from './create-person.body.params.js';
import { CreatePersonenkontextBodyParams } from './create-personenkontext.body.params.js';
import { CreatedPersonenkontextDto } from './created-personenkontext.dto.js';
import { PersonApiMapperProfile } from './person-api.mapper.profile.js';
import { PersonBirthParams } from './person-birth.params.js';
import { PersonByIdParams } from './person-by-id.param.js';
import { PersonController } from './person.controller.js';
import { PersonDto } from './person.dto.js';
import { PersonUc } from './person.uc.js';
import { PersonByIdParams } from './person-by-id.param.js';
import { HttpException } from '@nestjs/common';
import { PersonenQueryParams, SichtfreigabeType } from './personen-query.param.js';
import { PersonBirthParams } from './person-birth.params.js';
import { Geschlecht, Vertrauensstufe } from '../domain/person.enums.js';
import { PersonenQueryParams } from './personen-query.param.js';
import { PersonendatensatzDto } from './personendatensatz.dto.js';
import { PersonendatensatzResponse } from './personendatensatz.response.js';
import { PersonenkontextUc } from './personenkontext.uc.js';
import { CreatePersonenkontextBodyParams } from './create-personenkontext.body.params.js';
import { CreatedPersonenkontextDto } from './created-personenkontext.dto.js';
import { Jahrgangsstufe, Personenstatus, Rolle } from '../domain/personenkontext.enums.js';
import { PagedResponse } from '../../../shared/paging/index.js';
import { PersonenkontextResponse } from './personenkontext.response.js';
import { PersonenkontextQueryParams } from './personenkontext-query.params.js';
import { PersonendatensatzDto } from './personendatensatz.dto.js';
import { PersonDto } from './person.dto.js';
import { PersonenkontextDto } from './personenkontext.dto.js';
import { PersonenkontextResponse } from './personenkontext.response.js';
import { PersonenkontextUc } from './personenkontext.uc.js';

describe('PersonController', () => {
let module: TestingModule;
Expand Down Expand Up @@ -226,6 +226,7 @@ describe('PersonController', () => {
};
const personenkontextResponse: PersonenkontextDto = {
id: faker.string.uuid(),
personId: faker.string.uuid(),
organisation: {
id: faker.string.uuid(),
},
Expand All @@ -237,18 +238,23 @@ describe('PersonController', () => {
personenstatus: Personenstatus.AKTIV,
loeschung: { zeitpunkt: faker.date.past() },
};
const personenkontextDtos: PersonenkontextDto[] = [personenkontextResponse];
const personenkontextDtos: Paged<PersonenkontextDto> = {
items: [personenkontextResponse],
total: 1,
offset: 0,
limit: 1,
};

personenkontextUcMock.findAll.mockResolvedValue(personenkontextDtos);

const result: PersonenkontextResponse[] = await personController.findPersonenkontexte(
const result: PagedResponse<PersonenkontextResponse> = await personController.findPersonenkontexte(
pathParams,
queryParams,
);

expect(personenkontextUcMock.findAll).toHaveBeenCalledTimes(1);
expect(result.length).toBe(1);
expect(result[0]?.id).toBe(personenkontextDtos[0]?.id);
expect(result.items.length).toBe(1);
expect(result.items[0]?.id).toBe(personenkontextDtos.items[0]?.id);
});
});
});
Expand Down
34 changes: 20 additions & 14 deletions src/modules/person/api/person.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import {
Body,
Controller,
Get,
Inject,
Post,
Param,
HttpCode,
HttpException,
HttpStatus,
Query,
HttpCode,
Inject,
Param,
Patch,
Post,
Query,
UseInterceptors,
} from '@nestjs/common';
import {
Expand All @@ -27,9 +27,10 @@ import {
} from '@nestjs/swagger';
import { Public } from 'nest-keycloak-connect';
import { Paged, PagedResponse, PagingHeadersObject } from '../../../shared/paging/index.js';
import { ResultInterceptor } from '../../../shared/util/result-interceptor.js';
import { PersonUc } from '../api/person.uc.js';
import { CreatePersonDto } from './create-person.dto.js';
import { CreatePersonBodyParams } from './create-person.body.params.js';
import { CreatePersonDto } from './create-person.dto.js';
import { CreatePersonenkontextBodyParams } from './create-personenkontext.body.params.js';
import { CreatePersonenkontextDto } from './create-personenkontext.dto.js';
import { CreatedPersonenkontextDto } from './created-personenkontext.dto.js';
Expand All @@ -40,10 +41,9 @@ import { PersonenQueryParams } from './personen-query.param.js';
import { PersonendatensatzDto } from './personendatensatz.dto.js';
import { PersonendatensatzResponse } from './personendatensatz.response.js';
import { PersonenkontextQueryParams } from './personenkontext-query.params.js';
import { PersonenkontextDto } from './personenkontext.dto.js';
import { PersonenkontextResponse } from './personenkontext.response.js';
import { PersonenkontextUc } from './personenkontext.uc.js';
import { ResultInterceptor } from '../../../shared/util/result-interceptor.js';
import { PersonenkontextDto } from './personenkontext.dto.js';

@ApiTags('personen')
@Controller({ path: 'personen' })
Expand Down Expand Up @@ -111,15 +111,15 @@ export class PersonController {
}

@Get(':personId/personenkontexte')
@ApiOkResponse({ description: 'The personenkontexte were successfully pulled.' })
@ApiOkResponse({ description: 'The personenkontexte were successfully pulled.', headers: PagingHeadersObject })
@ApiUnauthorizedResponse({ description: 'Not authorized to get personenkontexte.' })
@ApiForbiddenResponse({ description: 'Insufficient permissions to get personenkontexte.' })
@ApiNotFoundResponse({ description: 'No personenkontexte were found.' })
@ApiInternalServerErrorResponse({ description: 'Internal server error while getting all personenkontexte.' })
public async findPersonenkontexte(
@Param() pathParams: PersonByIdParams,
@Query() queryParams: PersonenkontextQueryParams,
): Promise<PersonenkontextResponse[]> {
): Promise<PagedResponse<PersonenkontextResponse>> {
const findPersonenkontextDto: FindPersonenkontextDto = this.mapper.map(
queryParams,
PersonenkontextQueryParams,
Expand All @@ -128,15 +128,21 @@ export class PersonController {

findPersonenkontextDto.personId = pathParams.personId;

const personenkontextDtos: PersonenkontextDto[] = await this.personenkontextUc.findAll(findPersonenkontextDto);
const personenkontextDtos: Paged<PersonenkontextDto> =
await this.personenkontextUc.findAll(findPersonenkontextDto);
// AI next 5 lines
const response: PersonenkontextResponse[] = this.mapper.mapArray(
personenkontextDtos,
const responseItems: PersonenkontextResponse[] = this.mapper.mapArray(
personenkontextDtos.items,
PersonenkontextDto,
PersonenkontextResponse,
);

return response;
return new PagedResponse({
items: responseItems,
offset: personenkontextDtos.offset,
limit: personenkontextDtos.limit,
total: personenkontextDtos.total,
});
}

@Get()
Expand Down
Loading

0 comments on commit b92837e

Please sign in to comment.