diff --git a/src/modules/person/api/created-personenkontext.dto.ts b/src/modules/person/api/created-personenkontext.dto.ts index 0a9f04331..7d135cac6 100644 --- a/src/modules/person/api/created-personenkontext.dto.ts +++ b/src/modules/person/api/created-personenkontext.dto.ts @@ -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() diff --git a/src/modules/person/api/find-personendatensatz.dto.ts b/src/modules/person/api/find-personendatensatz.dto.ts index 16eee1f92..c47355efc 100644 --- a/src/modules/person/api/find-personendatensatz.dto.ts +++ b/src/modules/person/api/find-personendatensatz.dto.ts @@ -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() diff --git a/src/modules/person/api/find-personenkontext.dto.ts b/src/modules/person/api/find-personenkontext.dto.ts index 2074e975a..e1500bc42 100644 --- a/src/modules/person/api/find-personenkontext.dto.ts +++ b/src/modules/person/api/find-personenkontext.dto.ts @@ -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; diff --git a/src/modules/person/api/person-api.mapper.profile.spec.ts b/src/modules/person/api/person-api.mapper.profile.spec.ts index 7d2f90964..db2390385 100644 --- a/src/modules/person/api/person-api.mapper.profile.spec.ts +++ b/src/modules/person/api/person-api.mapper.profile.spec.ts @@ -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'; @@ -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; @@ -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 = { @@ -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); + }); }); }); diff --git a/src/modules/person/api/person-api.mapper.profile.ts b/src/modules/person/api/person-api.mapper.profile.ts index 96ce9d358..dd3b32164 100644 --- a/src/modules/person/api/person-api.mapper.profile.ts +++ b/src/modules/person/api/person-api.mapper.profile.ts @@ -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'; @@ -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 = { - 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 { @@ -241,6 +222,7 @@ export class PersonApiMapperProfile extends AutomapperProfile { forMember((dest: PersonenkontextDo) => dest.organisation, ignore()), forMember((dest: PersonenkontextDo) => dest.loeschungZeitpunkt, ignore()), forMember((dest: PersonenkontextDo) => dest.revision, ignore()), + forMember((dest: PersonenkontextDo) => dest.sichtfreigabe, ignore()), ); createMap( @@ -248,7 +230,7 @@ export class PersonApiMapperProfile extends AutomapperProfile { PersonenkontextDo, CreatedPersonenkontextDto, forMember( - (dest: PersonenkontextDto) => dest.loeschung, + (dest: CreatedPersonenkontextDto) => dest.loeschung, mapFrom((src: PersonenkontextDo) => src.loeschungZeitpunkt ? new LoeschungDto({ zeitpunkt: src.loeschungZeitpunkt }) : undefined, ), @@ -280,17 +262,6 @@ export class PersonApiMapperProfile extends AutomapperProfile { forMember((dest: PersonenkontextDo) => dest.id, ignore()), forMember((dest: PersonenkontextDo) => dest.createdAt, ignore()), forMember((dest: PersonenkontextDo) => dest.updatedAt, ignore()), - forMember( - (dest: PersonenkontextDo) => dest.id, - mapFrom((src: FindPersonenkontextDto) => src.personId), - ), - forMember( - (dest: PersonenkontextDo) => dest.sichtfreigabe, - convertUsing( - personVisibilityToBooleanConverter, - (src: FindPersonenkontextDto) => src.sichtfreigabe, - ), - ), ); createMap(mapper, FindPersonenkontextByIdParams, FindPersonenkontextByIdDto); @@ -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), + ]), + ), + ); }; } } diff --git a/src/modules/person/api/person-id.response.ts b/src/modules/person/api/person-id.response.ts new file mode 100644 index 000000000..6e029a018 --- /dev/null +++ b/src/modules/person/api/person-id.response.ts @@ -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) { + Object.assign(this, props); + } +} diff --git a/src/modules/person/api/person.controller.spec.ts b/src/modules/person/api/person.controller.spec.ts index de1d98a5c..65c32a7d0 100644 --- a/src/modules/person/api/person.controller.spec.ts +++ b/src/modules/person/api/person.controller.spec.ts @@ -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; @@ -226,6 +226,7 @@ describe('PersonController', () => { }; const personenkontextResponse: PersonenkontextDto = { id: faker.string.uuid(), + personId: faker.string.uuid(), organisation: { id: faker.string.uuid(), }, @@ -237,18 +238,23 @@ describe('PersonController', () => { personenstatus: Personenstatus.AKTIV, loeschung: { zeitpunkt: faker.date.past() }, }; - const personenkontextDtos: PersonenkontextDto[] = [personenkontextResponse]; + const personenkontextDtos: Paged = { + items: [personenkontextResponse], + total: 1, + offset: 0, + limit: 1, + }; personenkontextUcMock.findAll.mockResolvedValue(personenkontextDtos); - const result: PersonenkontextResponse[] = await personController.findPersonenkontexte( + const result: PagedResponse = 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); }); }); }); diff --git a/src/modules/person/api/person.controller.ts b/src/modules/person/api/person.controller.ts index 172c7e7cf..8d0e7c752 100644 --- a/src/modules/person/api/person.controller.ts +++ b/src/modules/person/api/person.controller.ts @@ -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 { @@ -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'; @@ -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' }) @@ -111,7 +111,7 @@ 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.' }) @@ -119,7 +119,7 @@ export class PersonController { public async findPersonenkontexte( @Param() pathParams: PersonByIdParams, @Query() queryParams: PersonenkontextQueryParams, - ): Promise { + ): Promise> { const findPersonenkontextDto: FindPersonenkontextDto = this.mapper.map( queryParams, PersonenkontextQueryParams, @@ -128,15 +128,21 @@ export class PersonController { findPersonenkontextDto.personId = pathParams.personId; - const personenkontextDtos: PersonenkontextDto[] = await this.personenkontextUc.findAll(findPersonenkontextDto); + const personenkontextDtos: Paged = + 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() diff --git a/src/modules/person/api/person.uc.spec.ts b/src/modules/person/api/person.uc.spec.ts index 0df02b50c..6dad575b6 100644 --- a/src/modules/person/api/person.uc.spec.ts +++ b/src/modules/person/api/person.uc.spec.ts @@ -5,14 +5,14 @@ import { DoFactory, MapperTestModule } from '../../../../test/utils/index.js'; import { EntityNotFoundError, KeycloakClientError, PersonAlreadyExistsError } from '../../../shared/error/index.js'; import { Paged } from '../../../shared/paging/index.js'; import { KeycloakUserService } from '../../keycloak-administration/index.js'; -import { CreatePersonDto } from './create-person.dto.js'; import { PersonDo } from '../domain/person.do.js'; import { PersonService } from '../domain/person.service.js'; +import { SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { PersonenkontextService } from '../domain/personenkontext.service.js'; +import { CreatePersonDto } from './create-person.dto.js'; import { FindPersonendatensatzDto } from './find-personendatensatz.dto.js'; import { PersonApiMapperProfile } from './person-api.mapper.profile.js'; import { PersonUc } from './person.uc.js'; -import { SichtfreigabeType } from './personen-query.param.js'; import { PersonendatensatzDto } from './personendatensatz.dto.js'; describe('PersonUc', () => { @@ -131,8 +131,10 @@ describe('PersonUc', () => { }); personenkontextServiceMock.findAllPersonenkontexte.mockResolvedValue({ - ok: true, - value: [DoFactory.createPersonenkontext(true)], + items: [DoFactory.createPersonenkontext(true)], + total: 1, + limit: 1, + offset: 0, }); await expect(personUc.findPersonById(id)).resolves.not.toThrow(); expect(personenkontextServiceMock.findAllPersonenkontexte).toHaveBeenCalledTimes(1); @@ -157,8 +159,10 @@ describe('PersonUc', () => { }); personenkontextServiceMock.findAllPersonenkontexte.mockResolvedValue({ - ok: false, - error: new EntityNotFoundError('Personenkontext'), + items: [], + total: 0, + limit: 0, + offset: 0, }); await expect(personUc.findPersonById(id)).resolves.not.toThrow(); expect(personenkontextServiceMock.findAllPersonenkontexte).toHaveBeenCalledTimes(1); @@ -186,8 +190,10 @@ describe('PersonUc', () => { personServiceMock.findAllPersons.mockResolvedValue(persons); personenkontextServiceMock.findAllPersonenkontexte.mockResolvedValue({ - ok: true, - value: [DoFactory.createPersonenkontext(true)], + items: [DoFactory.createPersonenkontext(true)], + total: 1, + limit: 1, + offset: 0, }); const result: Paged = await personUc.findAll(personDTO); diff --git a/src/modules/person/api/person.uc.ts b/src/modules/person/api/person.uc.ts index 36438267d..b1ebf1e4d 100644 --- a/src/modules/person/api/person.uc.ts +++ b/src/modules/person/api/person.uc.ts @@ -1,18 +1,17 @@ import { Mapper } from '@automapper/core'; import { getMapperToken } from '@automapper/nestjs'; import { Inject, Injectable } from '@nestjs/common'; -import { DomainError } from '../../../shared/error/domain.error.js'; import { Paged } from '../../../shared/paging/index.js'; import { KeycloakUserService, UserDo } from '../../keycloak-administration/index.js'; -import { CreatePersonDto } from './create-person.dto.js'; import { PersonDo } from '../domain/person.do.js'; import { PersonService } from '../domain/person.service.js'; import { PersonenkontextDo } from '../domain/personenkontext.do.js'; +import { SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { PersonenkontextService } from '../domain/personenkontext.service.js'; +import { CreatePersonDto } from './create-person.dto.js'; import { FindPersonendatensatzDto } from './find-personendatensatz.dto.js'; import { FindPersonenkontextDto } from './find-personenkontext.dto.js'; import { PersonDto } from './person.dto.js'; -import { SichtfreigabeType } from './personen-query.param.js'; import { PersonendatensatzDto } from './personendatensatz.dto.js'; import { PersonenkontextDto } from './personenkontext.dto.js'; @@ -126,17 +125,15 @@ export class PersonUc { personId: personId, sichtfreigabe: sichtfreigabe, }; - const result: Result[], DomainError> = - await this.personenkontextService.findAllPersonenkontexte( - this.mapper.map(personenkontextFilter, FindPersonenkontextDto, PersonenkontextDo), - ); - if (!result.ok) { - return []; - } + const result: Paged> = await this.personenkontextService.findAllPersonenkontexte( + this.mapper.map(personenkontextFilter, FindPersonenkontextDto, PersonenkontextDo), + ); - const personenkontextDtos: PersonenkontextDto[] = result.value.map((personenkontext: PersonenkontextDo) => - this.mapper.map(personenkontext, PersonenkontextDo, PersonenkontextDto), + const personenkontextDtos: PersonenkontextDto[] = this.mapper.mapArray( + result.items, + PersonenkontextDo, + PersonenkontextDto, ); return personenkontextDtos; diff --git a/src/modules/person/api/personen-query.param.spec.ts b/src/modules/person/api/personen-query.param.spec.ts index 678ac6e7b..29a4ff25c 100644 --- a/src/modules/person/api/personen-query.param.spec.ts +++ b/src/modules/person/api/personen-query.param.spec.ts @@ -1,7 +1,8 @@ +import { faker } from '@faker-js/faker'; import { plainToInstance } from 'class-transformer'; import 'reflect-metadata'; -import { PersonenQueryParams, SichtfreigabeType } from './personen-query.param.js'; -import { faker } from '@faker-js/faker'; +import { SichtfreigabeType } from '../domain/personenkontext.enums.js'; +import { PersonenQueryParams } from './personen-query.param.js'; describe('PersonenQueryParam', () => { const referenceParams: PersonenQueryParams = { diff --git a/src/modules/person/api/personen-query.param.ts b/src/modules/person/api/personen-query.param.ts index 895d22f01..080be1701 100644 --- a/src/modules/person/api/personen-query.param.ts +++ b/src/modules/person/api/personen-query.param.ts @@ -1,12 +1,8 @@ +import { AutoMap } from '@automapper/classes'; import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsOptional, IsString } from 'class-validator'; -import { AutoMap } from '@automapper/classes'; import { PagedQueryParams } from '../../../shared/paging/index.js'; - -export enum SichtfreigabeType { - JA = 'ja', - NEIN = 'nein', -} +import { SichtfreigabeType } from '../domain/personenkontext.enums.js'; export class PersonenQueryParams extends PagedQueryParams { @AutoMap() @@ -36,7 +32,7 @@ export class PersonenQueryParams extends PagedQueryParams { }) public readonly vorname?: string; - @AutoMap() + @AutoMap(() => String) @IsOptional() @IsEnum(SichtfreigabeType) @ApiProperty({ diff --git a/src/modules/person/api/personenkontext-query.params.spec.ts b/src/modules/person/api/personenkontext-query.params.spec.ts index 14ca3b8c5..1477fea51 100644 --- a/src/modules/person/api/personenkontext-query.params.spec.ts +++ b/src/modules/person/api/personenkontext-query.params.spec.ts @@ -1,8 +1,7 @@ import { plainToInstance } from 'class-transformer'; import 'reflect-metadata'; +import { Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { PersonenkontextQueryParams } from './personenkontext-query.params.js'; -import { SichtfreigabeType } from './personen-query.param.js'; -import { Personenstatus, Rolle } from '../domain/personenkontext.enums.js'; describe('PersonenkontextQueryParams', () => { const referenceParams: PersonenkontextQueryParams = { diff --git a/src/modules/person/api/personenkontext-query.params.ts b/src/modules/person/api/personenkontext-query.params.ts index 310ea1dc4..10ce9b852 100644 --- a/src/modules/person/api/personenkontext-query.params.ts +++ b/src/modules/person/api/personenkontext-query.params.ts @@ -1,10 +1,10 @@ import { AutoMap } from '@automapper/classes'; import { ApiProperty } from '@nestjs/swagger'; import { IsEnum, IsOptional, IsString } from 'class-validator'; -import { Personenstatus, Rolle } from '../domain/personenkontext.enums.js'; -import { SichtfreigabeType } from './personen-query.param.js'; +import { PagedQueryParams } from '../../../shared/paging/paged.query.params.js'; +import { Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; -export class PersonenkontextQueryParams { +export class PersonenkontextQueryParams extends PagedQueryParams { @AutoMap() @IsOptional() @IsString() @@ -14,25 +14,27 @@ export class PersonenkontextQueryParams { }) public readonly referrer?: string; - @AutoMap() + @AutoMap(() => String) @IsOptional() @IsEnum(Rolle) @ApiProperty({ required: false, nullable: true, + enum: Rolle, }) public readonly rolle?: Rolle; - @AutoMap() + @AutoMap(() => String) @IsOptional() @IsEnum(Personenstatus) @ApiProperty({ required: false, nullable: true, + enum: Personenstatus, }) public readonly personenstatus?: Personenstatus; - @AutoMap() + @AutoMap(() => String) @IsOptional() @IsEnum(SichtfreigabeType) @ApiProperty({ diff --git a/src/modules/person/api/personenkontext.controller.spec.ts b/src/modules/person/api/personenkontext.controller.spec.ts index c90caeae1..98dc3fc1c 100644 --- a/src/modules/person/api/personenkontext.controller.spec.ts +++ b/src/modules/person/api/personenkontext.controller.spec.ts @@ -1,16 +1,22 @@ 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 { PersonenkontextController } from './personenkontext.controller.js'; -import { PersonenkontextUc } from './personenkontext.uc.js'; +import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; +import { Paged } from '../../../shared/paging/paged.js'; +import { PagedResponse } from '../../../shared/paging/paged.response.js'; +import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { FindPersonenkontextByIdParams } from './find-personenkontext-by-id.params.js'; import { PersonApiMapperProfile } from './person-api.mapper.profile.js'; -import { PersonendatensatzResponse } from './personendatensatz.response.js'; -import { PersonendatensatzDto } from './personendatensatz.dto.js'; import { PersonDto } from './person.dto.js'; -import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; -import { HttpException } from '@nestjs/common'; +import { PersonendatensatzDto } from './personendatensatz.dto.js'; +import { PersonendatensatzResponse } from './personendatensatz.response.js'; +import { PersonenkontextQueryParams } from './personenkontext-query.params.js'; +import { PersonenkontextController } from './personenkontext.controller.js'; +import { PersonenkontextDto } from './personenkontext.dto.js'; +import { PersonenkontextUc } from './personenkontext.uc.js'; +import { PersonenkontextdatensatzResponse } from './personenkontextdatensatz.response.js'; describe('PersonenkontextController', () => { let module: TestingModule; @@ -87,4 +93,49 @@ describe('PersonenkontextController', () => { }); }); }); + + describe('findPersonenkontexte', () => { + describe('when finding personenkontexte', () => { + it('should return personenkontext', async () => { + const queryParams: PersonenkontextQueryParams = { + referrer: 'referrer', + sichtfreigabe: SichtfreigabeType.JA, + personenstatus: Personenstatus.AKTIV, + rolle: Rolle.LERNENDER, + offset: 0, + limit: 10, + }; + const personenkontext: PersonenkontextDto = { + id: faker.string.uuid(), + personId: faker.string.uuid(), + mandant: faker.string.uuid(), + referrer: queryParams.referrer, + sichtfreigabe: queryParams.sichtfreigabe, + rolle: queryParams.rolle ?? Rolle.LERNENDER, + jahrgangsstufe: Jahrgangsstufe.JAHRGANGSSTUFE_1, + personenstatus: Personenstatus.AKTIV, + organisation: { + id: faker.string.uuid(), + }, + revision: '1', + }; + const personenkontexte: Paged = { + offset: queryParams.offset ?? 0, + limit: queryParams.limit ?? 1, + total: 1, + items: [personenkontext], + }; + personenkontextUcMock.findAll.mockResolvedValue(personenkontexte); + + const result: PagedResponse = + await sut.findPersonenkontexte(queryParams); + + expect(personenkontextUcMock.findAll).toBeCalledTimes(1); + expect(result.items.length).toBe(1); + expect(result.items[0]?.person.id).toBe(personenkontext.personId); + expect(result.items[0]?.personenkontexte.length).toBe(1); + expect(result.items[0]?.personenkontexte[0]?.id).toBe(personenkontext.id); + }); + }); + }); }); diff --git a/src/modules/person/api/personenkontext.controller.ts b/src/modules/person/api/personenkontext.controller.ts index 8a9b7645c..a8661d2e8 100644 --- a/src/modules/person/api/personenkontext.controller.ts +++ b/src/modules/person/api/personenkontext.controller.ts @@ -1,6 +1,6 @@ import { Mapper } from '@automapper/core'; import { getMapperToken } from '@automapper/nestjs'; -import { Controller, Get, HttpException, HttpStatus, Inject, Param } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Inject, Param, Query } from '@nestjs/common'; import { ApiBadRequestResponse, ApiForbiddenResponse, @@ -12,11 +12,18 @@ import { } from '@nestjs/swagger'; import { Public } from 'nest-keycloak-connect'; import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; +import { Paged } from '../../../shared/paging/paged.js'; +import { PagedResponse } from '../../../shared/paging/paged.response.js'; import { FindPersonenkontextByIdDto } from './find-personenkontext-by-id.dto.js'; import { FindPersonenkontextByIdParams } from './find-personenkontext-by-id.params.js'; +import { FindPersonenkontextDto } from './find-personenkontext.dto.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 { PersonenkontextUc } from './personenkontext.uc.js'; +import { PersonenkontextdatensatzResponse } from './personenkontextdatensatz.response.js'; +import { PagingHeadersObject } from '../../../shared/paging/paging.enums.js'; @Public() @ApiTags('personenkontexte') @@ -62,4 +69,39 @@ export class PersonenkontextController { throw error; } } + + @Get() + @ApiOkResponse({ + description: 'The personenkontexte were successfully returned.', + type: [PersonendatensatzResponse], + headers: PagingHeadersObject, + }) + @ApiBadRequestResponse({ description: 'Request has wrong format.' }) + @ApiUnauthorizedResponse({ description: 'Request is not authorized.' }) + @ApiNotFoundResponse({ description: 'The personenkontexte were not found.' }) + @ApiForbiddenResponse({ description: 'Insufficient permissions to perform operation.' }) + @ApiInternalServerErrorResponse({ description: 'An internal server error occurred.' }) + public async findPersonenkontexte( + @Query() queryParams: PersonenkontextQueryParams, + ): Promise> { + const findPersonenkontextDto: FindPersonenkontextDto = this.mapper.map( + queryParams, + PersonenkontextQueryParams, + FindPersonenkontextDto, + ); + const result: Paged = await this.personenkontextUc.findAll(findPersonenkontextDto); + const responseItems: PersonenkontextdatensatzResponse[] = this.mapper.mapArray( + result.items, + PersonenkontextDto, + PersonenkontextdatensatzResponse, + ); + const response: PagedResponse = new PagedResponse({ + items: responseItems, + total: result.total, + offset: result.offset, + limit: result.limit, + }); + + return response; + } } diff --git a/src/modules/person/api/personenkontext.dto.ts b/src/modules/person/api/personenkontext.dto.ts index f5c10aac0..6aa703656 100644 --- a/src/modules/person/api/personenkontext.dto.ts +++ b/src/modules/person/api/personenkontext.dto.ts @@ -1,13 +1,15 @@ import { AutoMap } from '@automapper/classes'; -import { Rolle, Personenstatus, Jahrgangsstufe } from '../domain/personenkontext.enums.js'; +import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { CreatedPersonenkontextOrganisationDto } from './created-personenkontext-organisation.dto.js'; -import { SichtfreigabeType } from './personen-query.param.js'; import { LoeschungDto } from './loeschung.dto.js'; export class PersonenkontextDto { @AutoMap() public readonly id!: string; + @AutoMap() + public readonly personId!: string; + @AutoMap() public readonly referrer?: string; diff --git a/src/modules/person/api/personenkontext.response.ts b/src/modules/person/api/personenkontext.response.ts index e4bb47959..c3bc49538 100644 --- a/src/modules/person/api/personenkontext.response.ts +++ b/src/modules/person/api/personenkontext.response.ts @@ -1,9 +1,8 @@ import { AutoMap } from '@automapper/classes'; import { ApiProperty } from '@nestjs/swagger'; -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 { LoeschungResponse } from './loeschung.response.js'; -import { SichtfreigabeType } from './personen-query.param.js'; export class PersonenkontextResponse { @AutoMap() diff --git a/src/modules/person/api/personenkontext.uc.spec.ts b/src/modules/person/api/personenkontext.uc.spec.ts index d4b612e46..3fc8995a7 100644 --- a/src/modules/person/api/personenkontext.uc.spec.ts +++ b/src/modules/person/api/personenkontext.uc.spec.ts @@ -2,20 +2,19 @@ import { faker } from '@faker-js/faker'; import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { DoFactory, MapperTestModule } from '../../../../test/utils/index.js'; -import { DomainError } from '../../../shared/error/domain.error.js'; import { EntityCouldNotBeCreated } from '../../../shared/error/entity-could-not-be-created.error.js'; import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; +import { Paged } from '../../../shared/paging/paged.js'; import { PersonDo } from '../domain/person.do.js'; import { PersonService } from '../domain/person.service.js'; import { PersonenkontextDo } from '../domain/personenkontext.do.js'; -import { Personenstatus, Rolle } from '../domain/personenkontext.enums.js'; +import { Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { PersonenkontextService } from '../domain/personenkontext.service.js'; import { CreatePersonenkontextDto } from './create-personenkontext.dto.js'; import { CreatedPersonenkontextDto } from './created-personenkontext.dto.js'; import { FindPersonenkontextByIdDto } from './find-personenkontext-by-id.dto.js'; import { FindPersonenkontextDto } from './find-personenkontext.dto.js'; import { PersonApiMapperProfile } from './person-api.mapper.profile.js'; -import { SichtfreigabeType } from './personen-query.param.js'; import { PersonenkontextDto } from './personenkontext.dto.js'; import { PersonenkontextUc } from './personenkontext.uc.js'; @@ -107,15 +106,17 @@ describe('PersonenkontextUc', () => { const secondPersonenkontext: PersonenkontextDo = DoFactory.createPersonenkontext(true); const personenkontexte: PersonenkontextDo[] = [firstPersonenkontext, secondPersonenkontext]; personenkontextServiceMock.findAllPersonenkontexte.mockResolvedValue({ - ok: true, - value: personenkontexte, + items: personenkontexte, + total: personenkontexte.length, + limit: personenkontexte.length, + offset: 0, }); - const result: PersonenkontextDto[] = await sut.findAll(findPersonenkontextDto); - expect(result).toHaveLength(2); + const result: Paged = await sut.findAll(findPersonenkontextDto); + expect(result.items).toHaveLength(2); }); - it('should throw EntityNotFoundError when no matching persons are found', async () => { + it('should return empty array when no matching persons are found', async () => { const findPersonenkontextDto: FindPersonenkontextDto = { personId: faker.string.uuid(), referrer: 'referrer', @@ -124,12 +125,17 @@ describe('PersonenkontextUc', () => { rolle: Rolle.LERNENDER, }; - const emptyResult: Result[], DomainError> = { - ok: false, - error: new EntityNotFoundError('Personenkontext'), + const emptyResult: Paged> = { + items: [], + total: 0, + limit: 0, + offset: 0, }; personenkontextServiceMock.findAllPersonenkontexte.mockResolvedValue(emptyResult); - await expect(sut.findAll(findPersonenkontextDto)).rejects.toThrow(EntityNotFoundError); + + const result: Paged = await sut.findAll(findPersonenkontextDto); + + expect(result.items).toHaveLength(0); }); }); }); diff --git a/src/modules/person/api/personenkontext.uc.ts b/src/modules/person/api/personenkontext.uc.ts index 2931dc0aa..f20078de3 100644 --- a/src/modules/person/api/personenkontext.uc.ts +++ b/src/modules/person/api/personenkontext.uc.ts @@ -1,7 +1,7 @@ import { Mapper } from '@automapper/core'; import { getMapperToken } from '@automapper/nestjs'; import { Inject, Injectable } from '@nestjs/common'; -import { DomainError } from '../../../shared/error/domain.error.js'; +import { Paged } from '../../../shared/paging/paged.js'; import { PersonDo } from '../domain/person.do.js'; import { PersonService } from '../domain/person.service.js'; import { PersonenkontextDo } from '../domain/personenkontext.do.js'; @@ -38,24 +38,31 @@ export class PersonenkontextUc { throw result.error; } - // TODO refactor after EW-561 is done - public async findAll(findPersonenkontextDto: FindPersonenkontextDto): Promise { + public async findAll(findPersonenkontextDto: FindPersonenkontextDto): Promise> { const personenkontextDo: PersonenkontextDo = this.mapper.map( findPersonenkontextDto, FindPersonenkontextDto, PersonenkontextDo, ); - const result: Result[], DomainError> = - await this.personenkontextService.findAllPersonenkontexte(personenkontextDo); - if (!result.ok) { - throw result.error; - } + const result: Paged> = await this.personenkontextService.findAllPersonenkontexte( + personenkontextDo, + findPersonenkontextDto.offset, + findPersonenkontextDto.limit, + ); - const personenkontexte: PersonenkontextDto[] = result.value.map((personenkontext: PersonenkontextDo) => - this.mapper.map(personenkontext, PersonenkontextDo, PersonenkontextDto), + const personenkontexte: PersonenkontextDto[] = this.mapper.mapArray( + result.items, + PersonenkontextDo, + PersonenkontextDto, ); - return personenkontexte; + + return { + total: result.total, + offset: result.offset, + limit: result.limit, + items: personenkontexte, + }; } public async findPersonenkontextById(dto: FindPersonenkontextByIdDto): Promise { diff --git a/src/modules/person/api/personenkontextdatensatz.response.ts b/src/modules/person/api/personenkontextdatensatz.response.ts new file mode 100644 index 000000000..ae591f35a --- /dev/null +++ b/src/modules/person/api/personenkontextdatensatz.response.ts @@ -0,0 +1,14 @@ +import { AutoMap } from '@automapper/classes'; +import { ApiProperty } from '@nestjs/swagger'; +import { PersonIdResponse } from './person-id.response.js'; +import { PersonenkontextResponse } from './personenkontext.response.js'; + +export class PersonenkontextdatensatzResponse { + @AutoMap(() => PersonIdResponse) + @ApiProperty({ type: PersonIdResponse }) + public person!: PersonIdResponse; + + @AutoMap(() => [PersonenkontextResponse]) + @ApiProperty({ type: [PersonenkontextResponse] }) + public personenkontexte!: PersonenkontextResponse[]; +} diff --git a/src/modules/person/domain/personenkontext.do.ts b/src/modules/person/domain/personenkontext.do.ts index dba5a534e..0ab592d9f 100644 --- a/src/modules/person/domain/personenkontext.do.ts +++ b/src/modules/person/domain/personenkontext.do.ts @@ -1,7 +1,7 @@ import { AutoMap } from '@automapper/classes'; import { DoBase } from '../../../shared/types/index.js'; import { OrganisationDo } from '../../organisation/domain/organisation.do.js'; -import { Jahrgangsstufe, Personenstatus, Rolle } from './personenkontext.enums.js'; +import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from './personenkontext.enums.js'; export class PersonenkontextDo implements DoBase { /** @@ -40,8 +40,8 @@ export class PersonenkontextDo implements DoBase String) public jahrgangsstufe?: Jahrgangsstufe; - @AutoMap() - public sichtfreigabe?: boolean = false; + @AutoMap(() => String) + public sichtfreigabe?: SichtfreigabeType; @AutoMap(() => Date) public loeschungZeitpunkt?: Date; diff --git a/src/modules/person/domain/personenkontext.enums.ts b/src/modules/person/domain/personenkontext.enums.ts index 70391841e..810e35b7f 100644 --- a/src/modules/person/domain/personenkontext.enums.ts +++ b/src/modules/person/domain/personenkontext.enums.ts @@ -23,3 +23,8 @@ export enum Rolle { ORGANISATIONSLEITUNG = 'LEIT', SYSTEMADMINISTRATOR = 'SYSADMIN', } + +export enum SichtfreigabeType { + JA = 'ja', + NEIN = 'nein', +} diff --git a/src/modules/person/domain/personenkontext.service.spec.ts b/src/modules/person/domain/personenkontext.service.spec.ts index 0cd0bca49..b82ca5e83 100644 --- a/src/modules/person/domain/personenkontext.service.spec.ts +++ b/src/modules/person/domain/personenkontext.service.spec.ts @@ -5,12 +5,13 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DoFactory } from '../../../../test/utils/do-factory.js'; import { DomainError } from '../../../shared/error/domain.error.js'; import { EntityCouldNotBeCreated } from '../../../shared/error/entity-could-not-be-created.error.js'; +import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; +import { Paged } from '../../../shared/paging/paged.js'; import { PersonRepo } from '../persistence/person.repo.js'; import { PersonenkontextRepo } from '../persistence/personenkontext.repo.js'; +import { PersonDo } from './person.do.js'; import { PersonenkontextDo } from './personenkontext.do.js'; import { PersonenkontextService } from './personenkontext.service.js'; -import { EntityNotFoundError } from '../../../shared/error/entity-not-found.error.js'; -import { PersonDo } from './person.do.js'; describe('PersonenkontextService', () => { let module: TestingModule; @@ -118,16 +119,20 @@ describe('PersonenkontextService', () => { personenkontext1 as unknown as PersonenkontextDo, personenkontext2 as unknown as PersonenkontextDo, ]; - personenkontextRepoMock.findAll.mockResolvedValue(personenkontexte); + personenkontextRepoMock.findBy.mockResolvedValue([personenkontexte, personenkontexte.length]); mapperMock.map.mockReturnValue(personenkontexte as unknown as Dictionary); const personenkontextDoWithQueryParam: PersonenkontextDo = DoFactory.createPersonenkontext(false); - const result: Result[], DomainError> = - await personenkontextService.findAllPersonenkontexte(personenkontextDoWithQueryParam); - expect(result).toEqual[], DomainError>>({ - ok: true, - value: personenkontexte, + const result: Paged> = await personenkontextService.findAllPersonenkontexte( + personenkontextDoWithQueryParam, + ); + + expect(result).toEqual>>({ + items: personenkontexte, + total: personenkontexte.length, + offset: 0, + limit: personenkontexte.length, }); }); }); @@ -135,13 +140,17 @@ describe('PersonenkontextService', () => { describe('When no personenkontexte are found', () => { it('should return a result with an empty array', async () => { const personenkontext: PersonenkontextDo = DoFactory.createPersonenkontext(false); - personenkontextRepoMock.findAll.mockResolvedValue([]); + personenkontextRepoMock.findBy.mockResolvedValue([[], 0]); mapperMock.map.mockReturnValue(personenkontext as unknown as Dictionary); - const result: Result[], DomainError> = + + const result: Paged> = await personenkontextService.findAllPersonenkontexte(personenkontext); - expect(result).toEqual[], DomainError>>({ - ok: true, - value: [], + + expect(result).toEqual>>({ + items: [], + total: 0, + offset: 0, + limit: 0, }); }); }); diff --git a/src/modules/person/domain/personenkontext.service.ts b/src/modules/person/domain/personenkontext.service.ts index 89eff0b82..bd7e47e83 100644 --- a/src/modules/person/domain/personenkontext.service.ts +++ b/src/modules/person/domain/personenkontext.service.ts @@ -1,10 +1,13 @@ import { Injectable } from '@nestjs/common'; import { EntityCouldNotBeCreated } from '../../../shared/error/entity-could-not-be-created.error.js'; import { DomainError, EntityNotFoundError } from '../../../shared/error/index.js'; +import { Paged } from '../../../shared/paging/paged.js'; +import { ScopeOrder } from '../../../shared/persistence/scope.enums.js'; import { PersonRepo } from '../persistence/person.repo.js'; import { PersonenkontextRepo } from '../persistence/personenkontext.repo.js'; -import { PersonenkontextDo } from './personenkontext.do.js'; +import { PersonenkontextScope } from '../persistence/personenkontext.scope.js'; import { PersonDo } from './person.do.js'; +import { PersonenkontextDo } from './personenkontext.do.js'; @Injectable() export class PersonenkontextService { @@ -30,10 +33,29 @@ export class PersonenkontextService { public async findAllPersonenkontexte( personenkontextDo: PersonenkontextDo, - ): Promise[], DomainError>> { - const personenkontexte: PersonenkontextDo[] = await this.personenkontextRepo.findAll(personenkontextDo); + offset?: number, + limit?: number, + ): Promise>> { + const scope: PersonenkontextScope = new PersonenkontextScope() + .findBy({ + personId: personenkontextDo.personId, + referrer: personenkontextDo.referrer, + rolle: personenkontextDo.rolle, + personenstatus: personenkontextDo.personenstatus, + sichtfreigabe: personenkontextDo.sichtfreigabe, + }) + .sortBy('id', ScopeOrder.ASC) + .paged(offset, limit); + + const [personenkontexte, total]: Counted> = + await this.personenkontextRepo.findBy(scope); - return { ok: true, value: personenkontexte }; + return { + offset: offset ?? 0, + limit: limit ?? total, + total, + items: personenkontexte, + }; } public async findPersonenkontextById(id: string): Promise, DomainError>> { diff --git a/src/modules/person/persistence/personenkontext.entity.ts b/src/modules/person/persistence/personenkontext.entity.ts index 04cd09eef..fb47842ab 100644 --- a/src/modules/person/persistence/personenkontext.entity.ts +++ b/src/modules/person/persistence/personenkontext.entity.ts @@ -1,7 +1,7 @@ import { AutoMap } from '@automapper/classes'; import { DateTimeType, Entity, Enum, Property } from '@mikro-orm/core'; import { TimestampedEntity } from '../../../persistence/timestamped.entity.js'; -import { Rolle, Personenstatus, Jahrgangsstufe } from '../domain/personenkontext.enums.js'; +import { Jahrgangsstufe, Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; @Entity({ tableName: 'personenkontext' }) export class PersonenkontextEntity extends TimestampedEntity { @@ -39,9 +39,9 @@ export class PersonenkontextEntity extends TimestampedEntity Jahrgangsstufe }) public jahrgangsstufe?: Jahrgangsstufe; - @AutoMap() - @Property({ nullable: true }) - public sichtfreigabe?: boolean = false; + @AutoMap(() => String) + @Property({ nullable: true, default: SichtfreigabeType.NEIN }) + public sichtfreigabe?: SichtfreigabeType; @AutoMap(() => Date) @Property({ nullable: true, type: DateTimeType }) diff --git a/src/modules/person/persistence/personenkontext.repo.integration-spec.ts b/src/modules/person/persistence/personenkontext.repo.integration-spec.ts index d8ba998b7..a96616371 100644 --- a/src/modules/person/persistence/personenkontext.repo.integration-spec.ts +++ b/src/modules/person/persistence/personenkontext.repo.integration-spec.ts @@ -12,11 +12,12 @@ import { } from '../../../../test/utils/index.js'; import { PersonDo } from '../domain/person.do.js'; import { PersonenkontextDo } from '../domain/personenkontext.do.js'; -import { Personenstatus, Rolle } from '../domain/personenkontext.enums.js'; +import { Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; import { PersonPersistenceMapperProfile } from './person-persistence.mapper.profile.js'; import { PersonEntity } from './person.entity.js'; import { PersonenkontextEntity } from './personenkontext.entity.js'; import { PersonenkontextRepo } from './personenkontext.repo.js'; +import { PersonenkontextScope } from './personenkontext.scope.js'; describe('PersonenkontextRepo', () => { let module: TestingModule; @@ -111,14 +112,14 @@ describe('PersonenkontextRepo', () => { }); }); - describe('findAll', () => { + describe('findBy', () => { describe('When personenkontext for person exists', () => { it('should find all personenkontexte for this person', async () => { const props: Partial> = { referrer: 'referrer', personenstatus: Personenstatus.AKTIV, rolle: Rolle.LERNENDER, - sichtfreigabe: false, + sichtfreigabe: SichtfreigabeType.NEIN, }; const person1Id: string = faker.string.uuid(); const personenkontextDo1: PersonenkontextDo = DoFactory.createPersonenkontext(false, { @@ -129,15 +130,15 @@ describe('PersonenkontextRepo', () => { await em.persistAndFlush(mapper.map(personenkontextDo1, PersonenkontextDo, PersonenkontextEntity)); await em.persistAndFlush(mapper.map(personenkontextDo2, PersonenkontextDo, PersonenkontextEntity)); - const personenkontextDoFromQueryParam: PersonenkontextDo = DoFactory.createPersonenkontext( - false, - { - ...props, + const [result]: Counted> = await sut.findBy( + new PersonenkontextScope().findBy({ + referrer: 'referrer', + personenstatus: Personenstatus.AKTIV, + rolle: Rolle.LERNENDER, + sichtfreigabe: SichtfreigabeType.NEIN, personId: person1Id, - }, + }), ); - - const result: PersonenkontextDo[] = await sut.findAll(personenkontextDoFromQueryParam); expect(result).not.toBeNull(); expect(result).toHaveLength(1); await expect(em.find(PersonenkontextEntity, {})).resolves.toHaveLength(2); @@ -146,12 +147,8 @@ describe('PersonenkontextRepo', () => { describe('When no personenkontext matches', () => { it('should return an empty list', async () => { - const props: Partial> = {}; - const personenkontextDoFromQueryParam: PersonenkontextDo = DoFactory.createPersonenkontext( - false, - props, - ); - const result: PersonenkontextDo[] = await sut.findAll(personenkontextDoFromQueryParam); + const [result]: Counted> = await sut.findBy(new PersonenkontextScope()); + expect(result).not.toBeNull(); expect(result).toHaveLength(0); await expect(em.find(PersonenkontextEntity, {})).resolves.toHaveLength(0); diff --git a/src/modules/person/persistence/personenkontext.repo.ts b/src/modules/person/persistence/personenkontext.repo.ts index ac708f868..8cca74185 100644 --- a/src/modules/person/persistence/personenkontext.repo.ts +++ b/src/modules/person/persistence/personenkontext.repo.ts @@ -5,6 +5,7 @@ import { EntityManager } from '@mikro-orm/postgresql'; import { Inject, Injectable } from '@nestjs/common'; import { PersonenkontextDo } from '../domain/personenkontext.do.js'; import { PersonenkontextEntity } from './personenkontext.entity.js'; +import { PersonenkontextScope } from './personenkontext.scope.js'; @Injectable() export class PersonenkontextRepo { @@ -20,32 +21,11 @@ export class PersonenkontextRepo { return this.create(personenkontextDo); } - // TODO refactor after EW-561 is done, use Scope - public async findAll(personenkontextDo: PersonenkontextDo): Promise[]> { - const query: Record = {}; + public async findBy(scope: PersonenkontextScope): Promise>> { + const [entities, total]: Counted = await scope.executeQuery(this.em); + const dos: PersonenkontextDo[] = this.mapper.mapArray(entities, PersonenkontextEntity, PersonenkontextDo); - query['personId'] = personenkontextDo.personId; - - if (personenkontextDo.referrer) { - query['referrer'] = personenkontextDo.referrer; - } - - if (personenkontextDo.rolle) { - query['rolle'] = personenkontextDo.rolle; - } - - if (personenkontextDo.personenstatus) { - query['personenstatus'] = personenkontextDo.personenstatus; - } - - if (personenkontextDo.sichtfreigabe !== undefined) { - query['sichtfreigabe'] = personenkontextDo.sichtfreigabe; - } - - const result: PersonenkontextEntity[] = await this.em.find(PersonenkontextEntity, query); - return result.map((person: PersonenkontextEntity) => - this.mapper.map(person, PersonenkontextEntity, PersonenkontextDo), - ); + return [dos, total]; } public async findById(id: string): Promise>> { diff --git a/src/modules/person/persistence/personenkontext.scope.integration-spec.ts b/src/modules/person/persistence/personenkontext.scope.integration-spec.ts new file mode 100644 index 000000000..05d2e52c4 --- /dev/null +++ b/src/modules/person/persistence/personenkontext.scope.integration-spec.ts @@ -0,0 +1,69 @@ +import { Mapper } from '@automapper/core'; +import { getMapperToken } from '@automapper/nestjs'; +import { MikroORM } from '@mikro-orm/core'; +import { EntityManager } from '@mikro-orm/postgresql'; +import { Test, TestingModule } from '@nestjs/testing'; +import { + ConfigTestModule, + DEFAULT_TIMEOUT_FOR_TESTCONTAINERS, + DatabaseTestModule, + DoFactory, + MapperTestModule, +} from '../../../../test/utils/index.js'; +import { ScopeOrder } from '../../../shared/persistence/scope.enums.js'; +import { PersonenkontextDo } from '../domain/personenkontext.do.js'; +import { PersonPersistenceMapperProfile } from './person-persistence.mapper.profile.js'; +import { PersonenkontextEntity } from './personenkontext.entity.js'; +import { PersonenkontextScope } from './personenkontext.scope.js'; + +describe('PersonenkontextScope', () => { + let module: TestingModule; + let orm: MikroORM; + let em: EntityManager; + let mapper: Mapper; + + beforeAll(async () => { + module = await Test.createTestingModule({ + imports: [ConfigTestModule, DatabaseTestModule.forRoot({ isDatabaseRequired: true }), MapperTestModule], + providers: [PersonPersistenceMapperProfile], + }).compile(); + orm = module.get(MikroORM); + em = module.get(EntityManager); + mapper = module.get(getMapperToken()); + + await DatabaseTestModule.setupDatabase(orm); + }, DEFAULT_TIMEOUT_FOR_TESTCONTAINERS); + + afterAll(async () => { + await module.close(); + }); + + beforeEach(async () => { + await DatabaseTestModule.clearDatabase(orm); + }); + + describe('findBy', () => { + describe('when filtering for personenkontexte', () => { + beforeEach(async () => { + const dos: PersonenkontextDo[] = DoFactory.createMany( + 30, + false, + DoFactory.createPersonenkontext, + ); + + await em.persistAndFlush(mapper.mapArray(dos, PersonenkontextDo, PersonenkontextEntity)); + }); + + it('should return found personenkontexte', async () => { + const scope: PersonenkontextScope = new PersonenkontextScope() + .findBy({ referrer: 'referrer' }) + .sortBy('id', ScopeOrder.ASC) + .paged(10, 10); + const [personenkontext, total]: Counted = await scope.executeQuery(em); + + expect(total).toBe(30); + expect(personenkontext).toHaveLength(10); + }); + }); + }); +}); diff --git a/src/modules/person/persistence/personenkontext.scope.ts b/src/modules/person/persistence/personenkontext.scope.ts new file mode 100644 index 000000000..010aff79c --- /dev/null +++ b/src/modules/person/persistence/personenkontext.scope.ts @@ -0,0 +1,34 @@ +import { EntityName } from '@mikro-orm/core'; +import { ScopeBase } from '../../../shared/persistence/scope-base.js'; +import { ScopeOperator } from '../../../shared/persistence/scope.enums.js'; +import { Personenstatus, Rolle, SichtfreigabeType } from '../domain/personenkontext.enums.js'; +import { PersonenkontextEntity } from './personenkontext.entity.js'; + +type FindProps = { + personId: string; + referrer: string; + rolle: Rolle; + personenstatus: Personenstatus; + sichtfreigabe: SichtfreigabeType; +}; + +export class PersonenkontextScope extends ScopeBase { + public override get entityName(): EntityName { + return PersonenkontextEntity; + } + + public findBy(findProps: Findable, operator: ScopeOperator = ScopeOperator.AND): this { + this.findByInternal( + { + personId: findProps.personId, + referrer: findProps.referrer, + rolle: findProps.rolle, + personenstatus: findProps.personenstatus, + sichtfreigabe: findProps.sichtfreigabe, + }, + operator, + ); + + return this; + } +} diff --git a/test/utils/do-factory.ts b/test/utils/do-factory.ts index 078400ead..fc8a6d951 100644 --- a/test/utils/do-factory.ts +++ b/test/utils/do-factory.ts @@ -4,7 +4,12 @@ import { OrganisationDo } from '../../src/modules/organisation/domain/organisati import { OrganisationsTyp } from '../../src/modules/organisation/domain/organisation.enum.js'; import { PersonDo } from '../../src/modules/person/domain/person.do.js'; import { PersonenkontextDo } from '../../src/modules/person/domain/personenkontext.do.js'; -import { Jahrgangsstufe, Personenstatus, Rolle } from '../../src/modules/person/domain/personenkontext.enums.js'; +import { + Jahrgangsstufe, + Personenstatus, + Rolle, + SichtfreigabeType, +} from '../../src/modules/person/domain/personenkontext.enums.js'; import { PersonRollenZuweisungDo } from '../../src/modules/rolle/domain/person-rollen-zuweisung.do.js'; import { RolleBerechtigungsZuweisungDo } from '../../src/modules/rolle/domain/rolle-berechtigungs-zuweisung.do.js'; import { RolleRechtDo } from '../../src/modules/rolle/domain/rolle-recht.do.js'; @@ -91,7 +96,7 @@ export class DoFactory { jahrgangsstufe: Jahrgangsstufe.JAHRGANGSSTUFE_1, personenstatus: Personenstatus.AKTIV, referrer: 'referrer', - sichtfreigabe: true, + sichtfreigabe: SichtfreigabeType.JA, loeschungZeitpunkt: faker.date.anytime(), };