From 6d607a5dee5a7b4345ed38b98ada1b113525babc Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 28 Nov 2024 14:53:35 +0100 Subject: [PATCH 01/13] add time limit infos to api --- .../api/authentication.controller.ts | 12 ++++++ .../api/person-time-limit-info.reponse.ts | 15 ++++++++ .../authentication/api/userinfo.response.ts | 6 +++ .../authentication-api.module.ts | 2 + .../domain/person-time-limit-info.ts | 8 ++++ .../domain/time-limit-occasion.enums.ts | 3 ++ .../person-time-limit-info.service.ts | 37 +++++++++++++++++++ src/modules/person/api/person.controller.ts | 14 +++++++ .../domain/dbiam-personenkontext.service.ts | 16 ++++++++ 9 files changed, 113 insertions(+) create mode 100644 src/modules/authentication/api/person-time-limit-info.reponse.ts create mode 100644 src/modules/authentication/domain/person-time-limit-info.ts create mode 100644 src/modules/authentication/domain/time-limit-occasion.enums.ts create mode 100644 src/modules/authentication/services/person-time-limit-info.service.ts diff --git a/src/modules/authentication/api/authentication.controller.ts b/src/modules/authentication/api/authentication.controller.ts index bba9d1122..43f9d2019 100644 --- a/src/modules/authentication/api/authentication.controller.ts +++ b/src/modules/authentication/api/authentication.controller.ts @@ -31,6 +31,9 @@ import { AuthenticationExceptionFilter } from './authentication-exception-filter import { KeycloakUserService } from '../../keycloak-administration/index.js'; import { DomainError } from '../../../shared/error/domain.error.js'; import { getLowestStepUpLevel } from '../passport/oidc.strategy.js'; +import PersonTimeLimitService from '../services/person-time-limit-info.service.js'; +import { PersonTimeLimitInfo } from '../domain/person-time-limit-info.js'; +import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; @UseFilters(new AuthenticationExceptionFilter()) @ApiTags('auth') @@ -47,6 +50,7 @@ export class AuthenticationController { @Inject(OIDC_CLIENT) private client: Client, private readonly logger: ClassLogger, private keycloakUserService: KeycloakUserService, + private personTimeLimitService: PersonTimeLimitService, ) { const frontendConfig: FrontendConfig = configService.getOrThrow('FRONTEND'); const keycloakConfig: KeycloakConfig = configService.getOrThrow('KEYCLOAK'); @@ -132,10 +136,18 @@ export class AuthenticationController { if (lastPasswordChange.ok) userinfoExtension.password_updated_at = lastPasswordChange.value; } + const timeLimitInfos: PersonTimeLimitInfo[] = await this.personTimeLimitService.getPersonTimeLimitInfo( + permissions.personFields.id, + ); + const timeLimitInfosResponse: PersonTimeLimitInfoResponse[] = timeLimitInfos.map( + (info: PersonTimeLimitInfo) => new PersonTimeLimitInfoResponse(info.occasion, info.deadline), + ); + return new UserinfoResponse( permissions, rolleFieldsResponse, req.passportUser?.stepUpLevel ?? getLowestStepUpLevel(), + timeLimitInfosResponse, userinfoExtension, ); } diff --git a/src/modules/authentication/api/person-time-limit-info.reponse.ts b/src/modules/authentication/api/person-time-limit-info.reponse.ts new file mode 100644 index 000000000..723c54865 --- /dev/null +++ b/src/modules/authentication/api/person-time-limit-info.reponse.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; + +export class PersonTimeLimitInfoResponse { + @ApiProperty({ nullable: true }) + public occasion: TimeLimitOccasion; + + @ApiProperty({ nullable: true }) + public deadline: string; + + public constructor(occasion: TimeLimitOccasion, deadline: Date) { + this.occasion = occasion; + this.deadline = deadline?.toISOString(); + } +} diff --git a/src/modules/authentication/api/userinfo.response.ts b/src/modules/authentication/api/userinfo.response.ts index 260798a24..d2d856047 100644 --- a/src/modules/authentication/api/userinfo.response.ts +++ b/src/modules/authentication/api/userinfo.response.ts @@ -2,6 +2,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { PersonPermissions } from '../domain/person-permissions.js'; import { PersonenkontextRolleFieldsResponse } from './personen-kontext-rolle-fields.response.js'; import { StepUpLevel } from '../passport/oidc.strategy.js'; +import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; export type UserinfoExtension = { password_updated_at?: Date; @@ -74,10 +75,14 @@ export class UserinfoResponse { @ApiProperty({ nullable: false }) public acr: StepUpLevel; + @ApiProperty({ type: PersonTimeLimitInfoResponse, isArray: true }) + public timeLimits: PersonTimeLimitInfoResponse[]; + public constructor( info: PersonPermissions, personenkontexte: PersonenkontextRolleFieldsResponse[], acr: StepUpLevel, + timeLimits: PersonTimeLimitInfoResponse[], extension?: UserinfoExtension, ) { this.sub = info.personFields.keycloakUserId!; @@ -93,5 +98,6 @@ export class UserinfoResponse { this.personenkontexte = personenkontexte; this.password_updated_at = extension?.password_updated_at?.toISOString(); this.acr = acr; + this.timeLimits = timeLimits; } } diff --git a/src/modules/authentication/authentication-api.module.ts b/src/modules/authentication/authentication-api.module.ts index a38c26808..7e6cc18d0 100644 --- a/src/modules/authentication/authentication-api.module.ts +++ b/src/modules/authentication/authentication-api.module.ts @@ -13,6 +13,7 @@ import { JwtStrategy } from './passport/jwt.strategy.js'; import { OrganisationModule } from '../organisation/organisation.module.js'; import { RolleModule } from '../rolle/rolle.module.js'; import { KeycloakAdministrationModule } from '../keycloak-administration/keycloak-administration.module.js'; +import PersonTimeLimitService from './services/person-time-limit-info.service.js'; @Module({ imports: [ @@ -31,6 +32,7 @@ import { KeycloakAdministrationModule } from '../keycloak-administration/keycloa OIDCClientProvider, PersonPermissionsRepo, SessionAccessTokenMiddleware, + PersonTimeLimitService, ], controllers: [AuthenticationController], exports: [OIDCClientProvider, PersonPermissionsRepo], diff --git a/src/modules/authentication/domain/person-time-limit-info.ts b/src/modules/authentication/domain/person-time-limit-info.ts new file mode 100644 index 000000000..d2b53c73b --- /dev/null +++ b/src/modules/authentication/domain/person-time-limit-info.ts @@ -0,0 +1,8 @@ +import { TimeLimitOccasion } from './time-limit-occasion.enums.js'; + +export class PersonTimeLimitInfo { + public constructor( + public readonly occasion: TimeLimitOccasion, + public readonly deadline: Date, + ) {} +} diff --git a/src/modules/authentication/domain/time-limit-occasion.enums.ts b/src/modules/authentication/domain/time-limit-occasion.enums.ts new file mode 100644 index 000000000..d725c64f4 --- /dev/null +++ b/src/modules/authentication/domain/time-limit-occasion.enums.ts @@ -0,0 +1,3 @@ +export enum TimeLimitOccasion { + KOPERS = 'KOPERS', +} diff --git a/src/modules/authentication/services/person-time-limit-info.service.ts b/src/modules/authentication/services/person-time-limit-info.service.ts new file mode 100644 index 000000000..300a50885 --- /dev/null +++ b/src/modules/authentication/services/person-time-limit-info.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@nestjs/common'; +import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; +import { PersonTimeLimitInfo } from '../domain/person-time-limit-info.js'; +import { DBiamPersonenkontextService } from '../../personenkontext/domain/dbiam-personenkontext.service.js'; +import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; +import { PersonRepository } from '../../person/persistence/person.repository.js'; +import { Person } from '../../person/domain/person.js'; + +@Injectable() +export default class PersonTimeLimitService { + public constructor( + private readonly personRepository: PersonRepository, + private readonly dBiamPersonenkontextService: DBiamPersonenkontextService, + ) {} + + public async getPersonTimeLimitInfo(personId: string): Promise { + const person: Option> = await this.personRepository.findById(personId); + if (!person) { + return []; + } + const lockInfos: PersonTimeLimitInfo[] = []; + if ( + !person.personalnummer && + (await this.dBiamPersonenkontextService.isPersonalnummerRequiredForAnyPersonenkontextForPerson(person.id)) + ) { + const kopersKontext: Personenkontext | null = + await this.dBiamPersonenkontextService.getKopersPersonenkontext(person.id); + if (kopersKontext) { + const kopersdeadline: Date = new Date(kopersKontext.createdAt); + kopersdeadline.setDate(kopersdeadline.getDate() + 56); + lockInfos.push(new PersonTimeLimitInfo(TimeLimitOccasion.KOPERS, kopersdeadline)); + } + } + + return lockInfos; + } +} diff --git a/src/modules/person/api/person.controller.ts b/src/modules/person/api/person.controller.ts index 282bcf3b4..538255f43 100644 --- a/src/modules/person/api/person.controller.ts +++ b/src/modules/person/api/person.controller.ts @@ -208,6 +208,20 @@ export class PersonController { const personEmailResponse: Option = await this.emailRepo.getEmailAddressAndStatusForPerson( personResult.value, ); + + if ( + !personResult.value.personalnummer && + (await this.dBiamPersonenkontextService.isPersonalnummerRequiredForAnyPersonenkontextForPerson( + personResult.value.id, + )) + ) { + const kopersKontext: Personenkontext | null = + await this.dBiamPersonenkontextService.getKopersPersonenkontext(personResult.value.id); + if (kopersKontext) { + const koperslockDate: Date = new Date(kopersKontext.createdAt); + koperslockDate.setDate(koperslockDate.getDate() + 56); + } + } const response: PersonendatensatzResponse = new PersonendatensatzResponse( personResult.value, false, diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts index 61e577553..d5957316b 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts @@ -64,4 +64,20 @@ export class DBiamPersonenkontextService { rolle.merkmale.includes(RollenMerkmal.KOPERS_PFLICHT), ); } + + public async getKopersPersonenkontext(personId: string): Promise | null> { + const personenkontexte: Personenkontext[] = await this.dBiamPersonenkontextRepo.findByPerson(personId); + const uniqueRolleIds: Set = new Set(personenkontexte.map((pk: Personenkontext) => pk.rolleId)); + const foundRollen: Map> = await this.rolleRepo.findByIds(Array.from(uniqueRolleIds)); + + const kopersRolle: Rolle | undefined = Array.from(foundRollen.values()).find((rolle: Rolle) => + rolle.merkmale.includes(RollenMerkmal.KOPERS_PFLICHT), + ); + + if (!kopersRolle) { + return null; + } + + return personenkontexte.find((pk: Personenkontext) => pk.rolleId === kopersRolle.id) ?? null; + } } From 6fb0f8e27c3a506f0febe354dde1d19c19e64656 Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 29 Nov 2024 11:05:31 +0100 Subject: [PATCH 02/13] fix tests --- .../authentication.controller.integration-spec.ts | 5 +++++ .../authentication/api/userinfo.response.spec.ts | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/modules/authentication/api/authentication.controller.integration-spec.ts b/src/modules/authentication/api/authentication.controller.integration-spec.ts index 24214d9dd..da7fa0cd4 100644 --- a/src/modules/authentication/api/authentication.controller.integration-spec.ts +++ b/src/modules/authentication/api/authentication.controller.integration-spec.ts @@ -26,6 +26,7 @@ import { RolleRepo } from '../../rolle/repo/rolle.repo.js'; import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js'; import { KeycloakConfig } from '../../../shared/config/keycloak.config.js'; import { KeycloakUserService } from '../../keycloak-administration/index.js'; +import PersonTimeLimitService from '../services/person-time-limit-info.service.js'; describe('AuthenticationController', () => { let module: TestingModule; @@ -76,6 +77,10 @@ describe('AuthenticationController', () => { provide: KeycloakUserService, useValue: keycloakUserServiceMock, }, + { + provide: PersonTimeLimitService, + useValue: createMock(), + }, ], }).compile(); diff --git a/src/modules/authentication/api/userinfo.response.spec.ts b/src/modules/authentication/api/userinfo.response.spec.ts index 0c6119e73..87b1c1d5f 100644 --- a/src/modules/authentication/api/userinfo.response.spec.ts +++ b/src/modules/authentication/api/userinfo.response.spec.ts @@ -9,6 +9,8 @@ import { RolleRepo } from '../../rolle/repo/rolle.repo.js'; import { PersonenkontextRolleFieldsResponse } from './personen-kontext-rolle-fields.response.js'; import { createMock } from '@golevelup/ts-jest'; import { StepUpLevel } from '../passport/oidc.strategy.js'; +import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; +import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; describe('UserinfoResponse', () => { const permissions: PersonPermissions = new PersonPermissions( @@ -22,8 +24,15 @@ describe('UserinfoResponse', () => { rolle: { systemrechte: [faker.string.alpha()], serviceProviderIds: [faker.string.uuid()] }, }; + const personTimeLimtit: PersonTimeLimitInfoResponse = { + occasion: TimeLimitOccasion.KOPERS, + deadline: faker.date.future().toISOString(), + }; + it('constructs the object without optional extension', () => { - const userinfoResponse: UserinfoResponse = new UserinfoResponse(permissions, [pk], StepUpLevel.SILVER); + const userinfoResponse: UserinfoResponse = new UserinfoResponse(permissions, [pk], StepUpLevel.SILVER, [ + personTimeLimtit, + ]); expect(userinfoResponse).toBeDefined(); expect(userinfoResponse.password_updated_at).toBeUndefined(); }); @@ -34,6 +43,7 @@ describe('UserinfoResponse', () => { permissions, [pk], StepUpLevel.SILVER, + [personTimeLimtit], extension, ); expect(userinfoResponse).toBeDefined(); From 173317c02953067bce0d943e2544b75fff9a6ff4 Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 29 Nov 2024 15:34:31 +0100 Subject: [PATCH 03/13] add tests --- ...hentication.controller.integration-spec.ts | 11 ++- .../person-time-limit-info.service.spec.ts | 81 +++++++++++++++++++ .../dbiam-personenkontext.service.spec.ts | 56 +++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/modules/authentication/services/person-time-limit-info.service.spec.ts diff --git a/src/modules/authentication/api/authentication.controller.integration-spec.ts b/src/modules/authentication/api/authentication.controller.integration-spec.ts index da7fa0cd4..b2d7918f6 100644 --- a/src/modules/authentication/api/authentication.controller.integration-spec.ts +++ b/src/modules/authentication/api/authentication.controller.integration-spec.ts @@ -27,6 +27,7 @@ import { OrganisationRepository } from '../../organisation/persistence/organisat import { KeycloakConfig } from '../../../shared/config/keycloak.config.js'; import { KeycloakUserService } from '../../keycloak-administration/index.js'; import PersonTimeLimitService from '../services/person-time-limit-info.service.js'; +import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; describe('AuthenticationController', () => { let module: TestingModule; @@ -39,7 +40,7 @@ describe('AuthenticationController', () => { let rolleRepoMock: DeepMocked; const keycloakUserServiceMock: DeepMocked = createMock(); let keyCloakConfig: KeycloakConfig; - + let personTimeLimitServiceMock: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ imports: [ @@ -94,6 +95,7 @@ describe('AuthenticationController', () => { dbiamPersonenkontextRepoMock = module.get(DBiamPersonenkontextRepo); organisationRepoMock = module.get(OrganisationRepository); rolleRepoMock = module.get(RolleRepo); + personTimeLimitServiceMock = module.get(PersonTimeLimitService); }); afterEach(() => { @@ -280,6 +282,13 @@ describe('AuthenticationController', () => { value: person.updatedAt, }); + personTimeLimitServiceMock.getPersonTimeLimitInfo.mockResolvedValueOnce([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: faker.date.future(), + }, + ]); + const requestMock: Request = setupRequest(); const result: UserinfoResponse = await authController.info(permissions, requestMock); diff --git a/src/modules/authentication/services/person-time-limit-info.service.spec.ts b/src/modules/authentication/services/person-time-limit-info.service.spec.ts new file mode 100644 index 000000000..656baca4e --- /dev/null +++ b/src/modules/authentication/services/person-time-limit-info.service.spec.ts @@ -0,0 +1,81 @@ +import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { TestingModule, Test } from '@nestjs/testing'; +import { PersonRepository } from '../../person/persistence/person.repository.js'; +import PersonTimeLimitService from './person-time-limit-info.service.js'; +import { DBiamPersonenkontextService } from '../../personenkontext/domain/dbiam-personenkontext.service.js'; +import { Person } from '../../person/domain/person.js'; +import { DoFactory } from '../../../../test/utils/do-factory.js'; +import { PersonTimeLimitInfo } from '../domain/person-time-limit-info.js'; +import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; +import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; + +describe('PersonTimeLimitService', () => { + let module: TestingModule; + let sut: PersonTimeLimitService; + let personRepoMock: DeepMocked; + let dBiamPersonenkontextServiceMock: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + PersonTimeLimitService, + { + provide: PersonRepository, + useValue: createMock(), + }, + { + provide: DBiamPersonenkontextService, + useValue: createMock(), + }, + ], + }).compile(); + sut = module.get(PersonTimeLimitService); + personRepoMock = module.get(PersonRepository); + dBiamPersonenkontextServiceMock = module.get(DBiamPersonenkontextService); + }); + + afterAll(async () => { + await module.close(); + }); + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should be defined', () => { + expect(sut).toBeDefined(); + }); + + describe('getPersonTimeLimitInfo', () => { + it('should return PersonTimeLimitInfo array', async () => { + const person: Person = DoFactory.createPerson(true); + person.personalnummer = undefined; + personRepoMock.findById.mockResolvedValue(person); + + dBiamPersonenkontextServiceMock.isPersonalnummerRequiredForAnyPersonenkontextForPerson.mockResolvedValue( + true, + ); + const pesonenkontext: Personenkontext = DoFactory.createPersonenkontext(true); + dBiamPersonenkontextServiceMock.getKopersPersonenkontext.mockResolvedValue(pesonenkontext); + + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); + + const expectedDeadline: Date = new Date(pesonenkontext.createdAt); + expectedDeadline.setDate(expectedDeadline.getDate() + 56); + + expect(result).toEqual([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: expectedDeadline, + }, + ]); + }); + + it('should return empty array when person isnt found ', async () => { + personRepoMock.findById.mockResolvedValue(null); + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(''); + + expect(result).toEqual([]); + }); + }); +}); diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts index 5f22acb14..d42bbaed9 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts @@ -114,4 +114,60 @@ describe('DBiamPersonenkontextService', () => { }); }); }); + + describe('getKopersPersonenkontext', () => { + describe('when a person has a personenkontext with a rolle with koperspflichtig merkmale', () => { + it('should return the personenkontext', async () => { + const personenkontexte: Personenkontext[] = [ + personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), + personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '1'), + personenkontextFactory.construct('3', faker.date.past(), faker.date.recent(), '', '1', '1', '2'), + ]; + dbiamPersonenKontextRepoMock.findByPerson.mockResolvedValue(personenkontexte); + + const mapRollen: Map> = new Map(); + mapRollen.set( + '1', + DoFactory.createRolle(true, { + rollenart: RollenArt.LEHR, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + id: '1', + }), + ); + mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); + rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); + + const result: Personenkontext | null = await sut.getKopersPersonenkontext('1'); + expect(result).toEqual(personenkontexte[0]); + }); + }); + + describe('when a person has no personenkontext with a rolle with koperspflichtig merkmale', () => { + it('should return null', async () => { + const personenkontexte: Personenkontext[] = [ + personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), + personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '1'), + personenkontextFactory.construct('3', faker.date.past(), faker.date.recent(), '', '1', '1', '2'), + ]; + dbiamPersonenKontextRepoMock.findByPerson.mockResolvedValue(personenkontexte); + + const mapRollen: Map> = new Map(); + + mapRollen.set( + '1', + DoFactory.createRolle(true, { + rollenart: RollenArt.LERN, + merkmale: [], + id: '1', + }), + ); + + mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); + rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); + + const result: Personenkontext | null = await sut.getKopersPersonenkontext('1'); + expect(result).toBeNull(); + }); + }); + }); }); From ea8b0597e72e3371d81c372f2a8f07370a7a11d8 Mon Sep 17 00:00:00 2001 From: Timo K Date: Fri, 29 Nov 2024 15:57:19 +0100 Subject: [PATCH 04/13] change null return to undefined --- .../services/person-time-limit-info.service.ts | 2 +- src/modules/person/api/person.controller.ts | 2 +- .../domain/dbiam-personenkontext.service.spec.ts | 8 ++++---- .../domain/dbiam-personenkontext.service.ts | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/modules/authentication/services/person-time-limit-info.service.ts b/src/modules/authentication/services/person-time-limit-info.service.ts index 300a50885..041d9efdf 100644 --- a/src/modules/authentication/services/person-time-limit-info.service.ts +++ b/src/modules/authentication/services/person-time-limit-info.service.ts @@ -23,7 +23,7 @@ export default class PersonTimeLimitService { !person.personalnummer && (await this.dBiamPersonenkontextService.isPersonalnummerRequiredForAnyPersonenkontextForPerson(person.id)) ) { - const kopersKontext: Personenkontext | null = + const kopersKontext: Personenkontext | undefined = await this.dBiamPersonenkontextService.getKopersPersonenkontext(person.id); if (kopersKontext) { const kopersdeadline: Date = new Date(kopersKontext.createdAt); diff --git a/src/modules/person/api/person.controller.ts b/src/modules/person/api/person.controller.ts index 9aba45cb7..b4402a97a 100644 --- a/src/modules/person/api/person.controller.ts +++ b/src/modules/person/api/person.controller.ts @@ -214,7 +214,7 @@ export class PersonController { personResult.value.id, )) ) { - const kopersKontext: Personenkontext | null = + const kopersKontext: Personenkontext | undefined = await this.dBiamPersonenkontextService.getKopersPersonenkontext(personResult.value.id); if (kopersKontext) { const koperslockDate: Date = new Date(kopersKontext.createdAt); diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts index d42bbaed9..7616f767e 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts @@ -137,13 +137,13 @@ describe('DBiamPersonenkontextService', () => { mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); - const result: Personenkontext | null = await sut.getKopersPersonenkontext('1'); + const result: Personenkontext | undefined = await sut.getKopersPersonenkontext('1'); expect(result).toEqual(personenkontexte[0]); }); }); describe('when a person has no personenkontext with a rolle with koperspflichtig merkmale', () => { - it('should return null', async () => { + it('should return undefined', async () => { const personenkontexte: Personenkontext[] = [ personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '1'), @@ -165,8 +165,8 @@ describe('DBiamPersonenkontextService', () => { mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); - const result: Personenkontext | null = await sut.getKopersPersonenkontext('1'); - expect(result).toBeNull(); + const result: Personenkontext | undefined = await sut.getKopersPersonenkontext('1'); + expect(result).toBeUndefined(); }); }); }); diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts index d5957316b..ac78463b4 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts @@ -65,7 +65,7 @@ export class DBiamPersonenkontextService { ); } - public async getKopersPersonenkontext(personId: string): Promise | null> { + public async getKopersPersonenkontext(personId: string): Promise | undefined> { const personenkontexte: Personenkontext[] = await this.dBiamPersonenkontextRepo.findByPerson(personId); const uniqueRolleIds: Set = new Set(personenkontexte.map((pk: Personenkontext) => pk.rolleId)); const foundRollen: Map> = await this.rolleRepo.findByIds(Array.from(uniqueRolleIds)); @@ -75,9 +75,9 @@ export class DBiamPersonenkontextService { ); if (!kopersRolle) { - return null; + return undefined; } - return personenkontexte.find((pk: Personenkontext) => pk.rolleId === kopersRolle.id) ?? null; + return personenkontexte.find((pk: Personenkontext) => pk.rolleId === kopersRolle.id); } } From 239bf4263445e767fc7e32eb692f6dad9eb72fe4 Mon Sep 17 00:00:00 2001 From: Timo K Date: Mon, 9 Dec 2024 14:56:44 +0100 Subject: [PATCH 05/13] refactoring --- .../api/authentication.controller.integration-spec.ts | 4 ++-- .../authentication/api/authentication.controller.ts | 4 ++-- .../authentication/api/person-time-limit-info.reponse.ts | 2 +- src/modules/authentication/api/userinfo.response.spec.ts | 2 +- src/modules/authentication/authentication-api.module.ts | 2 +- src/modules/person/api/person.controller.ts | 3 ++- .../domain}/person-time-limit-info.service.spec.ts | 5 +++-- .../domain}/person-time-limit-info.service.ts | 7 ++++--- .../domain/person-time-limit-info.ts | 0 src/modules/person/domain/person-time-limit.ts | 1 + .../domain/time-limit-occasion.enums.ts | 0 src/modules/person/persistence/person.repository.ts | 5 +++-- 12 files changed, 20 insertions(+), 15 deletions(-) rename src/modules/{authentication/services => person/domain}/person-time-limit-info.service.spec.ts (95%) rename src/modules/{authentication/services => person/domain}/person-time-limit-info.service.ts (87%) rename src/modules/{authentication => person}/domain/person-time-limit-info.ts (100%) create mode 100644 src/modules/person/domain/person-time-limit.ts rename src/modules/{authentication => person}/domain/time-limit-occasion.enums.ts (100%) diff --git a/src/modules/authentication/api/authentication.controller.integration-spec.ts b/src/modules/authentication/api/authentication.controller.integration-spec.ts index b2d7918f6..9a99ba4b3 100644 --- a/src/modules/authentication/api/authentication.controller.integration-spec.ts +++ b/src/modules/authentication/api/authentication.controller.integration-spec.ts @@ -26,8 +26,8 @@ import { RolleRepo } from '../../rolle/repo/rolle.repo.js'; import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js'; import { KeycloakConfig } from '../../../shared/config/keycloak.config.js'; import { KeycloakUserService } from '../../keycloak-administration/index.js'; -import PersonTimeLimitService from '../services/person-time-limit-info.service.js'; -import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; +import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; +import PersonTimeLimitService from '../../person/domain/person-time-limit-info.service.js'; describe('AuthenticationController', () => { let module: TestingModule; diff --git a/src/modules/authentication/api/authentication.controller.ts b/src/modules/authentication/api/authentication.controller.ts index 43f9d2019..49ead3a6b 100644 --- a/src/modules/authentication/api/authentication.controller.ts +++ b/src/modules/authentication/api/authentication.controller.ts @@ -31,9 +31,9 @@ import { AuthenticationExceptionFilter } from './authentication-exception-filter import { KeycloakUserService } from '../../keycloak-administration/index.js'; import { DomainError } from '../../../shared/error/domain.error.js'; import { getLowestStepUpLevel } from '../passport/oidc.strategy.js'; -import PersonTimeLimitService from '../services/person-time-limit-info.service.js'; -import { PersonTimeLimitInfo } from '../domain/person-time-limit-info.js'; +import { PersonTimeLimitInfo } from '../../person/domain/person-time-limit-info.js'; import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; +import PersonTimeLimitService from '../../person/domain/person-time-limit-info.service.js'; @UseFilters(new AuthenticationExceptionFilter()) @ApiTags('auth') diff --git a/src/modules/authentication/api/person-time-limit-info.reponse.ts b/src/modules/authentication/api/person-time-limit-info.reponse.ts index 723c54865..c4c7cf841 100644 --- a/src/modules/authentication/api/person-time-limit-info.reponse.ts +++ b/src/modules/authentication/api/person-time-limit-info.reponse.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; +import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; export class PersonTimeLimitInfoResponse { @ApiProperty({ nullable: true }) diff --git a/src/modules/authentication/api/userinfo.response.spec.ts b/src/modules/authentication/api/userinfo.response.spec.ts index 87b1c1d5f..ff5aedc2a 100644 --- a/src/modules/authentication/api/userinfo.response.spec.ts +++ b/src/modules/authentication/api/userinfo.response.spec.ts @@ -10,7 +10,7 @@ import { PersonenkontextRolleFieldsResponse } from './personen-kontext-rolle-fie import { createMock } from '@golevelup/ts-jest'; import { StepUpLevel } from '../passport/oidc.strategy.js'; import { PersonTimeLimitInfoResponse } from './person-time-limit-info.reponse.js'; -import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; +import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; describe('UserinfoResponse', () => { const permissions: PersonPermissions = new PersonPermissions( diff --git a/src/modules/authentication/authentication-api.module.ts b/src/modules/authentication/authentication-api.module.ts index 7e6cc18d0..c92285ba4 100644 --- a/src/modules/authentication/authentication-api.module.ts +++ b/src/modules/authentication/authentication-api.module.ts @@ -13,7 +13,7 @@ import { JwtStrategy } from './passport/jwt.strategy.js'; import { OrganisationModule } from '../organisation/organisation.module.js'; import { RolleModule } from '../rolle/rolle.module.js'; import { KeycloakAdministrationModule } from '../keycloak-administration/keycloak-administration.module.js'; -import PersonTimeLimitService from './services/person-time-limit-info.service.js'; +import PersonTimeLimitService from '../person/domain/person-time-limit-info.service.js'; @Module({ imports: [ diff --git a/src/modules/person/api/person.controller.ts b/src/modules/person/api/person.controller.ts index b4402a97a..0a6deeab1 100644 --- a/src/modules/person/api/person.controller.ts +++ b/src/modules/person/api/person.controller.ts @@ -78,6 +78,7 @@ import { PersonEmailResponse } from './person-email-response.js'; import { UserLock } from '../../keycloak-administration/domain/user-lock.js'; import { StepUpGuard } from '../../authentication/api/steup-up.guard.js'; import { PersonLockOccasion } from '../domain/person.enums.js'; +import { KOPERS_DEADLINE_IN_DAYS } from '../domain/person-time-limit.js'; @UseFilters(SchulConnexValidationErrorFilter, new AuthenticationExceptionFilter(), new PersonExceptionFilter()) @ApiTags('personen') @@ -218,7 +219,7 @@ export class PersonController { await this.dBiamPersonenkontextService.getKopersPersonenkontext(personResult.value.id); if (kopersKontext) { const koperslockDate: Date = new Date(kopersKontext.createdAt); - koperslockDate.setDate(koperslockDate.getDate() + 56); + koperslockDate.setDate(koperslockDate.getDate() + KOPERS_DEADLINE_IN_DAYS); } } const response: PersonendatensatzResponse = new PersonendatensatzResponse( diff --git a/src/modules/authentication/services/person-time-limit-info.service.spec.ts b/src/modules/person/domain/person-time-limit-info.service.spec.ts similarity index 95% rename from src/modules/authentication/services/person-time-limit-info.service.spec.ts rename to src/modules/person/domain/person-time-limit-info.service.spec.ts index 656baca4e..84de41d18 100644 --- a/src/modules/authentication/services/person-time-limit-info.service.spec.ts +++ b/src/modules/person/domain/person-time-limit-info.service.spec.ts @@ -5,9 +5,10 @@ import PersonTimeLimitService from './person-time-limit-info.service.js'; import { DBiamPersonenkontextService } from '../../personenkontext/domain/dbiam-personenkontext.service.js'; import { Person } from '../../person/domain/person.js'; import { DoFactory } from '../../../../test/utils/do-factory.js'; -import { PersonTimeLimitInfo } from '../domain/person-time-limit-info.js'; import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; +import { PersonTimeLimitInfo } from './person-time-limit-info.js'; +import { KOPERS_DEADLINE_IN_DAYS } from './person-time-limit.js'; describe('PersonTimeLimitService', () => { let module: TestingModule; @@ -61,7 +62,7 @@ describe('PersonTimeLimitService', () => { const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); const expectedDeadline: Date = new Date(pesonenkontext.createdAt); - expectedDeadline.setDate(expectedDeadline.getDate() + 56); + expectedDeadline.setDate(expectedDeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); expect(result).toEqual([ { diff --git a/src/modules/authentication/services/person-time-limit-info.service.ts b/src/modules/person/domain/person-time-limit-info.service.ts similarity index 87% rename from src/modules/authentication/services/person-time-limit-info.service.ts rename to src/modules/person/domain/person-time-limit-info.service.ts index 041d9efdf..b1a7dae74 100644 --- a/src/modules/authentication/services/person-time-limit-info.service.ts +++ b/src/modules/person/domain/person-time-limit-info.service.ts @@ -1,10 +1,11 @@ import { Injectable } from '@nestjs/common'; import { Personenkontext } from '../../personenkontext/domain/personenkontext.js'; -import { PersonTimeLimitInfo } from '../domain/person-time-limit-info.js'; import { DBiamPersonenkontextService } from '../../personenkontext/domain/dbiam-personenkontext.service.js'; -import { TimeLimitOccasion } from '../domain/time-limit-occasion.enums.js'; import { PersonRepository } from '../../person/persistence/person.repository.js'; import { Person } from '../../person/domain/person.js'; +import { PersonTimeLimitInfo } from './person-time-limit-info.js'; +import { TimeLimitOccasion } from './time-limit-occasion.enums.js'; +import { KOPERS_DEADLINE_IN_DAYS } from './person-time-limit.js'; @Injectable() export default class PersonTimeLimitService { @@ -27,7 +28,7 @@ export default class PersonTimeLimitService { await this.dBiamPersonenkontextService.getKopersPersonenkontext(person.id); if (kopersKontext) { const kopersdeadline: Date = new Date(kopersKontext.createdAt); - kopersdeadline.setDate(kopersdeadline.getDate() + 56); + kopersdeadline.setDate(kopersdeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); lockInfos.push(new PersonTimeLimitInfo(TimeLimitOccasion.KOPERS, kopersdeadline)); } } diff --git a/src/modules/authentication/domain/person-time-limit-info.ts b/src/modules/person/domain/person-time-limit-info.ts similarity index 100% rename from src/modules/authentication/domain/person-time-limit-info.ts rename to src/modules/person/domain/person-time-limit-info.ts diff --git a/src/modules/person/domain/person-time-limit.ts b/src/modules/person/domain/person-time-limit.ts new file mode 100644 index 000000000..938b68853 --- /dev/null +++ b/src/modules/person/domain/person-time-limit.ts @@ -0,0 +1 @@ +export const KOPERS_DEADLINE_IN_DAYS: number = 56; diff --git a/src/modules/authentication/domain/time-limit-occasion.enums.ts b/src/modules/person/domain/time-limit-occasion.enums.ts similarity index 100% rename from src/modules/authentication/domain/time-limit-occasion.enums.ts rename to src/modules/person/domain/time-limit-occasion.enums.ts diff --git a/src/modules/person/persistence/person.repository.ts b/src/modules/person/persistence/person.repository.ts index cc497c079..bddbf8509 100644 --- a/src/modules/person/persistence/person.repository.ts +++ b/src/modules/person/persistence/person.repository.ts @@ -38,6 +38,7 @@ import { PersonalNummerForPersonWithTrailingSpaceError } from '../domain/persona import { VornameForPersonWithTrailingSpaceError } from '../domain/vorname-with-trailing-space.error.js'; import { SystemConfig } from '../../../shared/config/system.config.js'; import { UserLock } from '../../keycloak-administration/domain/user-lock.js'; +import { KOPERS_DEADLINE_IN_DAYS } from '../domain/person-time-limit.js'; /** * Return email-address for person, if an enabled email-address exists, return it. @@ -761,7 +762,7 @@ export class PersonRepository { public async getKoPersUserLockList(): Promise<[PersonID, string][]> { const daysAgo: Date = new Date(); - daysAgo.setDate(daysAgo.getDate() - 56); + daysAgo.setDate(daysAgo.getDate() - KOPERS_DEADLINE_IN_DAYS); const filters: QBFilterQuery = { $and: [ @@ -769,7 +770,7 @@ export class PersonRepository { { personenKontexte: { $some: { - createdAt: { $lte: daysAgo }, // Check that createdAt is older than 56 days + createdAt: { $lte: daysAgo }, // Check that createdAt is older than KOPERS_DEADLINE_IN_DAYS rolleId: { merkmale: { merkmal: RollenMerkmal.KOPERS_PFLICHT }, }, From 7cb7ad20c285682166cc3ede443968aaf46a8c53 Mon Sep 17 00:00:00 2001 From: Timo K Date: Wed, 11 Dec 2024 14:07:42 +0100 Subject: [PATCH 06/13] remove nullable --- .../authentication/api/person-time-limit-info.reponse.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/authentication/api/person-time-limit-info.reponse.ts b/src/modules/authentication/api/person-time-limit-info.reponse.ts index c4c7cf841..a3632ef32 100644 --- a/src/modules/authentication/api/person-time-limit-info.reponse.ts +++ b/src/modules/authentication/api/person-time-limit-info.reponse.ts @@ -2,14 +2,14 @@ import { ApiProperty } from '@nestjs/swagger'; import { TimeLimitOccasion } from '../../person/domain/time-limit-occasion.enums.js'; export class PersonTimeLimitInfoResponse { - @ApiProperty({ nullable: true }) + @ApiProperty() public occasion: TimeLimitOccasion; - @ApiProperty({ nullable: true }) + @ApiProperty() public deadline: string; public constructor(occasion: TimeLimitOccasion, deadline: Date) { this.occasion = occasion; - this.deadline = deadline?.toISOString(); + this.deadline = deadline.toISOString(); } } From d3e84ed52473d8f2adae41a0eee78a0fde0174a1 Mon Sep 17 00:00:00 2001 From: Timo K Date: Wed, 11 Dec 2024 14:31:17 +0100 Subject: [PATCH 07/13] import module not service --- .../authentication/authentication-api.module.ts | 2 -- src/modules/person/person.module.ts | 14 +++++++++++--- .../personenkontext/personenkontext.module.ts | 4 ++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/modules/authentication/authentication-api.module.ts b/src/modules/authentication/authentication-api.module.ts index c92285ba4..a38c26808 100644 --- a/src/modules/authentication/authentication-api.module.ts +++ b/src/modules/authentication/authentication-api.module.ts @@ -13,7 +13,6 @@ import { JwtStrategy } from './passport/jwt.strategy.js'; import { OrganisationModule } from '../organisation/organisation.module.js'; import { RolleModule } from '../rolle/rolle.module.js'; import { KeycloakAdministrationModule } from '../keycloak-administration/keycloak-administration.module.js'; -import PersonTimeLimitService from '../person/domain/person-time-limit-info.service.js'; @Module({ imports: [ @@ -32,7 +31,6 @@ import PersonTimeLimitService from '../person/domain/person-time-limit-info.serv OIDCClientProvider, PersonPermissionsRepo, SessionAccessTokenMiddleware, - PersonTimeLimitService, ], controllers: [AuthenticationController], exports: [OIDCClientProvider, PersonPermissionsRepo], diff --git a/src/modules/person/person.module.ts b/src/modules/person/person.module.ts index 45afb2505..a58a244b9 100644 --- a/src/modules/person/person.module.ts +++ b/src/modules/person/person.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '../../core/logging/logger.module.js'; import { PersonService } from './domain/person.service.js'; import { UsernameGeneratorService } from './domain/username-generator.service.js'; @@ -10,8 +10,15 @@ import { RolleFactory } from '../rolle/domain/rolle.factory.js'; import { ServiceProviderRepo } from '../service-provider/repo/service-provider.repo.js'; import { OrganisationRepository } from '../organisation/persistence/organisation.repository.js'; import { EventModule } from '../../core/eventbus/event.module.js'; +import PersonTimeLimitService from './domain/person-time-limit-info.service.js'; +import { PersonenKontextModule } from '../personenkontext/personenkontext.module.js'; @Module({ - imports: [KeycloakAdministrationModule, LoggerModule.register(PersonModule.name), EventModule], + imports: [ + KeycloakAdministrationModule, + LoggerModule.register(PersonModule.name), + EventModule, + forwardRef(() => PersonenKontextModule), + ], providers: [ PersonRepository, PersonService, @@ -21,7 +28,8 @@ import { EventModule } from '../../core/eventbus/event.module.js'; OrganisationRepository, RolleFactory, ServiceProviderRepo, + PersonTimeLimitService, ], - exports: [PersonService, PersonFactory, PersonRepository], + exports: [PersonService, PersonFactory, PersonRepository, PersonTimeLimitService], }) export class PersonModule {} diff --git a/src/modules/personenkontext/personenkontext.module.ts b/src/modules/personenkontext/personenkontext.module.ts index d717c0429..1479f88e2 100644 --- a/src/modules/personenkontext/personenkontext.module.ts +++ b/src/modules/personenkontext/personenkontext.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '../../core/logging/logger.module.js'; import { PersonenkontextRepo } from '../personenkontext/persistence/personenkontext.repo.js'; import { PersonenkontextService } from '../personenkontext/domain/personenkontext.service.js'; @@ -17,7 +17,7 @@ import { PersonenkontextWorkflowFactory } from './domain/personenkontext-workflo @Module({ imports: [ EventModule, - PersonModule, + forwardRef(() => PersonModule), RolleModule, OrganisationModule, LoggerModule.register(PersonenKontextModule.name), From 404f9186390223aa843e577f13b17db74d674a36 Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 12 Dec 2024 09:47:17 +0100 Subject: [PATCH 08/13] remove duplicate check --- .../person/domain/person-time-limit-info.service.spec.ts | 3 --- src/modules/person/domain/person-time-limit-info.service.ts | 5 +---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/modules/person/domain/person-time-limit-info.service.spec.ts b/src/modules/person/domain/person-time-limit-info.service.spec.ts index 84de41d18..753d9f148 100644 --- a/src/modules/person/domain/person-time-limit-info.service.spec.ts +++ b/src/modules/person/domain/person-time-limit-info.service.spec.ts @@ -53,9 +53,6 @@ describe('PersonTimeLimitService', () => { person.personalnummer = undefined; personRepoMock.findById.mockResolvedValue(person); - dBiamPersonenkontextServiceMock.isPersonalnummerRequiredForAnyPersonenkontextForPerson.mockResolvedValue( - true, - ); const pesonenkontext: Personenkontext = DoFactory.createPersonenkontext(true); dBiamPersonenkontextServiceMock.getKopersPersonenkontext.mockResolvedValue(pesonenkontext); diff --git a/src/modules/person/domain/person-time-limit-info.service.ts b/src/modules/person/domain/person-time-limit-info.service.ts index b1a7dae74..39961c042 100644 --- a/src/modules/person/domain/person-time-limit-info.service.ts +++ b/src/modules/person/domain/person-time-limit-info.service.ts @@ -20,10 +20,7 @@ export default class PersonTimeLimitService { return []; } const lockInfos: PersonTimeLimitInfo[] = []; - if ( - !person.personalnummer && - (await this.dBiamPersonenkontextService.isPersonalnummerRequiredForAnyPersonenkontextForPerson(person.id)) - ) { + if (!person.personalnummer) { const kopersKontext: Personenkontext | undefined = await this.dBiamPersonenkontextService.getKopersPersonenkontext(person.id); if (kopersKontext) { From 3bb946f93ff0d902881e1aa2995ffdd3ca906e80 Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 12 Dec 2024 10:38:21 +0100 Subject: [PATCH 09/13] fix test --- .../api/authentication.controller.integration-spec.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/authentication/api/authentication.controller.integration-spec.ts b/src/modules/authentication/api/authentication.controller.integration-spec.ts index 9a99ba4b3..2e094e8e7 100644 --- a/src/modules/authentication/api/authentication.controller.integration-spec.ts +++ b/src/modules/authentication/api/authentication.controller.integration-spec.ts @@ -40,7 +40,7 @@ describe('AuthenticationController', () => { let rolleRepoMock: DeepMocked; const keycloakUserServiceMock: DeepMocked = createMock(); let keyCloakConfig: KeycloakConfig; - let personTimeLimitServiceMock: DeepMocked; + const personTimeLimitServiceMock: DeepMocked = createMock(); beforeAll(async () => { module = await Test.createTestingModule({ imports: [ @@ -80,7 +80,7 @@ describe('AuthenticationController', () => { }, { provide: PersonTimeLimitService, - useValue: createMock(), + useValue: personTimeLimitServiceMock, }, ], }).compile(); @@ -95,7 +95,6 @@ describe('AuthenticationController', () => { dbiamPersonenkontextRepoMock = module.get(DBiamPersonenkontextRepo); organisationRepoMock = module.get(OrganisationRepository); rolleRepoMock = module.get(RolleRepo); - personTimeLimitServiceMock = module.get(PersonTimeLimitService); }); afterEach(() => { From cf19a88d1cf0ec890e778dbef5cbfaefa472eb3b Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 12 Dec 2024 11:20:46 +0100 Subject: [PATCH 10/13] remove unused code --- src/modules/person/api/person.controller.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/modules/person/api/person.controller.ts b/src/modules/person/api/person.controller.ts index 0a6deeab1..69cbcae05 100644 --- a/src/modules/person/api/person.controller.ts +++ b/src/modules/person/api/person.controller.ts @@ -78,7 +78,6 @@ import { PersonEmailResponse } from './person-email-response.js'; import { UserLock } from '../../keycloak-administration/domain/user-lock.js'; import { StepUpGuard } from '../../authentication/api/steup-up.guard.js'; import { PersonLockOccasion } from '../domain/person.enums.js'; -import { KOPERS_DEADLINE_IN_DAYS } from '../domain/person-time-limit.js'; @UseFilters(SchulConnexValidationErrorFilter, new AuthenticationExceptionFilter(), new PersonExceptionFilter()) @ApiTags('personen') @@ -209,19 +208,6 @@ export class PersonController { personResult.value, ); - if ( - !personResult.value.personalnummer && - (await this.dBiamPersonenkontextService.isPersonalnummerRequiredForAnyPersonenkontextForPerson( - personResult.value.id, - )) - ) { - const kopersKontext: Personenkontext | undefined = - await this.dBiamPersonenkontextService.getKopersPersonenkontext(personResult.value.id); - if (kopersKontext) { - const koperslockDate: Date = new Date(kopersKontext.createdAt); - koperslockDate.setDate(koperslockDate.getDate() + KOPERS_DEADLINE_IN_DAYS); - } - } const response: PersonendatensatzResponse = new PersonendatensatzResponse( personResult.value, false, From be9a08b3512e66e0a2fc3c44706ee6295722083a Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 12 Dec 2024 11:21:20 +0100 Subject: [PATCH 11/13] check for all koperskontexte and take the earliest one --- .../person-time-limit-info.service.spec.ts | 31 ++++++++++++- .../domain/person-time-limit-info.service.ts | 13 +++--- .../dbiam-personenkontext.service.spec.ts | 45 ++++++++++++++++--- .../domain/dbiam-personenkontext.service.ts | 12 +++-- 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/modules/person/domain/person-time-limit-info.service.spec.ts b/src/modules/person/domain/person-time-limit-info.service.spec.ts index 753d9f148..f860e0e10 100644 --- a/src/modules/person/domain/person-time-limit-info.service.spec.ts +++ b/src/modules/person/domain/person-time-limit-info.service.spec.ts @@ -54,7 +54,7 @@ describe('PersonTimeLimitService', () => { personRepoMock.findById.mockResolvedValue(person); const pesonenkontext: Personenkontext = DoFactory.createPersonenkontext(true); - dBiamPersonenkontextServiceMock.getKopersPersonenkontext.mockResolvedValue(pesonenkontext); + dBiamPersonenkontextServiceMock.getKopersPersonenkontexte.mockResolvedValue([pesonenkontext]); const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); @@ -69,6 +69,35 @@ describe('PersonTimeLimitService', () => { ]); }); + it('should return PersonTimeLimitInfo array with earliest Koperslock', async () => { + const person: Person = DoFactory.createPerson(true); + person.personalnummer = undefined; + personRepoMock.findById.mockResolvedValue(person); + + const personenkontext: Personenkontext = DoFactory.createPersonenkontext(true, { + createdAt: new Date('2021-01-02'), + }); + const personenkontext2: Personenkontext = DoFactory.createPersonenkontext(true, { + createdAt: new Date('2021-01-01'), + }); + dBiamPersonenkontextServiceMock.getKopersPersonenkontexte.mockResolvedValue([ + personenkontext, + personenkontext2, + ]); + + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); + + const expectedDeadline: Date = new Date(personenkontext2.createdAt); + expectedDeadline.setDate(expectedDeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); + + expect(result).toEqual([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: expectedDeadline, + }, + ]); + }); + it('should return empty array when person isnt found ', async () => { personRepoMock.findById.mockResolvedValue(null); const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(''); diff --git a/src/modules/person/domain/person-time-limit-info.service.ts b/src/modules/person/domain/person-time-limit-info.service.ts index 39961c042..8fd075792 100644 --- a/src/modules/person/domain/person-time-limit-info.service.ts +++ b/src/modules/person/domain/person-time-limit-info.service.ts @@ -21,15 +21,18 @@ export default class PersonTimeLimitService { } const lockInfos: PersonTimeLimitInfo[] = []; if (!person.personalnummer) { - const kopersKontext: Personenkontext | undefined = - await this.dBiamPersonenkontextService.getKopersPersonenkontext(person.id); - if (kopersKontext) { - const kopersdeadline: Date = new Date(kopersKontext.createdAt); + const kopersKontexte: Personenkontext[] = + await this.dBiamPersonenkontextService.getKopersPersonenkontexte(person.id); + if (kopersKontexte.length > 0) { + const earliestKopersKontext: Personenkontext = kopersKontexte.reduce( + (prev: Personenkontext, current: Personenkontext) => + prev.createdAt < current.createdAt ? prev : current, + ); + const kopersdeadline: Date = new Date(earliestKopersKontext.createdAt); kopersdeadline.setDate(kopersdeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); lockInfos.push(new PersonTimeLimitInfo(TimeLimitOccasion.KOPERS, kopersdeadline)); } } - return lockInfos; } } diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts index 7616f767e..90a666650 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.spec.ts @@ -115,7 +115,7 @@ describe('DBiamPersonenkontextService', () => { }); }); - describe('getKopersPersonenkontext', () => { + describe('getKopersPersonenkontexte', () => { describe('when a person has a personenkontext with a rolle with koperspflichtig merkmale', () => { it('should return the personenkontext', async () => { const personenkontexte: Personenkontext[] = [ @@ -137,8 +137,43 @@ describe('DBiamPersonenkontextService', () => { mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); - const result: Personenkontext | undefined = await sut.getKopersPersonenkontext('1'); - expect(result).toEqual(personenkontexte[0]); + const result: Personenkontext[] = await sut.getKopersPersonenkontexte('1'); + expect(result[0]).toEqual(personenkontexte[0]); + }); + }); + + describe('when a person has multiple personenkontexts with a rolle with koperspflichtig merkmale', () => { + it('should return the personenkontext', async () => { + const personenkontexte: Personenkontext[] = [ + personenkontextFactory.construct('1', faker.date.past(), faker.date.recent(), '', '1', '1', '1'), + personenkontextFactory.construct('2', faker.date.past(), faker.date.recent(), '', '1', '2', '2'), + personenkontextFactory.construct('3', faker.date.past(), faker.date.recent(), '', '1', '1', '3'), + ]; + dbiamPersonenKontextRepoMock.findByPerson.mockResolvedValue(personenkontexte); + + const mapRollen: Map> = new Map(); + mapRollen.set( + '1', + DoFactory.createRolle(true, { + rollenart: RollenArt.LEHR, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + id: '1', + }), + ); + mapRollen.set( + '2', + DoFactory.createRolle(true, { + rollenart: RollenArt.LEHR, + merkmale: [RollenMerkmal.KOPERS_PFLICHT], + id: '2', + }), + ); + rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); + + const result: Personenkontext[] = await sut.getKopersPersonenkontexte('1'); + expect(result.length).toBe(2); + expect(result[0]).toEqual(personenkontexte[0]); + expect(result[1]).toEqual(personenkontexte[1]); }); }); @@ -165,8 +200,8 @@ describe('DBiamPersonenkontextService', () => { mapRollen.set('2', DoFactory.createRolle(true, { rollenart: RollenArt.LEIT, merkmale: [], id: '2' })); rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen); - const result: Personenkontext | undefined = await sut.getKopersPersonenkontext('1'); - expect(result).toBeUndefined(); + const result: Personenkontext[] = await sut.getKopersPersonenkontexte('1'); + expect(result.length).toBe(0); }); }); }); diff --git a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts index ac78463b4..8c6d2c4e9 100644 --- a/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts +++ b/src/modules/personenkontext/domain/dbiam-personenkontext.service.ts @@ -65,19 +65,17 @@ export class DBiamPersonenkontextService { ); } - public async getKopersPersonenkontext(personId: string): Promise | undefined> { + public async getKopersPersonenkontexte(personId: string): Promise[]> { const personenkontexte: Personenkontext[] = await this.dBiamPersonenkontextRepo.findByPerson(personId); const uniqueRolleIds: Set = new Set(personenkontexte.map((pk: Personenkontext) => pk.rolleId)); const foundRollen: Map> = await this.rolleRepo.findByIds(Array.from(uniqueRolleIds)); - const kopersRolle: Rolle | undefined = Array.from(foundRollen.values()).find((rolle: Rolle) => + const kopersRolle: Rolle[] = Array.from(foundRollen.values()).filter((rolle: Rolle) => rolle.merkmale.includes(RollenMerkmal.KOPERS_PFLICHT), ); - if (!kopersRolle) { - return undefined; - } - - return personenkontexte.find((pk: Personenkontext) => pk.rolleId === kopersRolle.id); + return personenkontexte.filter((pk: Personenkontext) => + kopersRolle.some((rolle: Rolle) => rolle.id === pk.rolleId), + ); } } From 9beaadd7c274ca5717b16f5566fdd9b429cbfdff Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 12 Dec 2024 13:09:53 +0100 Subject: [PATCH 12/13] add second test case --- .../person-time-limit-info.service.spec.ts | 68 +++++++++++-------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/modules/person/domain/person-time-limit-info.service.spec.ts b/src/modules/person/domain/person-time-limit-info.service.spec.ts index f860e0e10..d415df71b 100644 --- a/src/modules/person/domain/person-time-limit-info.service.spec.ts +++ b/src/modules/person/domain/person-time-limit-info.service.spec.ts @@ -69,34 +69,46 @@ describe('PersonTimeLimitService', () => { ]); }); - it('should return PersonTimeLimitInfo array with earliest Koperslock', async () => { - const person: Person = DoFactory.createPerson(true); - person.personalnummer = undefined; - personRepoMock.findById.mockResolvedValue(person); - - const personenkontext: Personenkontext = DoFactory.createPersonenkontext(true, { - createdAt: new Date('2021-01-02'), - }); - const personenkontext2: Personenkontext = DoFactory.createPersonenkontext(true, { - createdAt: new Date('2021-01-01'), - }); - dBiamPersonenkontextServiceMock.getKopersPersonenkontexte.mockResolvedValue([ - personenkontext, - personenkontext2, - ]); - - const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); - - const expectedDeadline: Date = new Date(personenkontext2.createdAt); - expectedDeadline.setDate(expectedDeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); - - expect(result).toEqual([ - { - occasion: TimeLimitOccasion.KOPERS, - deadline: expectedDeadline, - }, - ]); - }); + it.each([ + { + personenkontextDates: ['2021-01-02', '2021-01-01'], + expectedDate: '2021-01-01', + }, + { + personenkontextDates: ['2021-01-01', '2021-01-02'], + expectedDate: '2021-01-01', + }, + ])( + 'should return PersonTimeLimitInfo array with earliest Koperslock', + async ({ + personenkontextDates, + expectedDate, + }: { + personenkontextDates: string[]; + expectedDate: string; + }) => { + const person: Person = DoFactory.createPerson(true); + person.personalnummer = undefined; + personRepoMock.findById.mockResolvedValue(person); + + const personenkontexte: Personenkontext[] = personenkontextDates.map((date: string) => + DoFactory.createPersonenkontext(true, { createdAt: new Date(date) }), + ); + dBiamPersonenkontextServiceMock.getKopersPersonenkontexte.mockResolvedValue(personenkontexte); + + const result: PersonTimeLimitInfo[] = await sut.getPersonTimeLimitInfo(person.id); + + const expectedDeadline: Date = new Date(expectedDate); + expectedDeadline.setDate(expectedDeadline.getDate() + KOPERS_DEADLINE_IN_DAYS); + + expect(result).toEqual([ + { + occasion: TimeLimitOccasion.KOPERS, + deadline: expectedDeadline, + }, + ]); + }, + ); it('should return empty array when person isnt found ', async () => { personRepoMock.findById.mockResolvedValue(null); From 93e7a4eec7dcb9f9a49caa54d384688f3b044219 Mon Sep 17 00:00:00 2001 From: Timo K Date: Thu, 12 Dec 2024 13:33:50 +0100 Subject: [PATCH 13/13] fix sonarcloud issues --- src/modules/authentication/api/authentication.controller.ts | 2 +- src/modules/person/domain/person-time-limit-info.service.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/authentication/api/authentication.controller.ts b/src/modules/authentication/api/authentication.controller.ts index 49ead3a6b..78a6da663 100644 --- a/src/modules/authentication/api/authentication.controller.ts +++ b/src/modules/authentication/api/authentication.controller.ts @@ -50,7 +50,7 @@ export class AuthenticationController { @Inject(OIDC_CLIENT) private client: Client, private readonly logger: ClassLogger, private keycloakUserService: KeycloakUserService, - private personTimeLimitService: PersonTimeLimitService, + private readonly personTimeLimitService: PersonTimeLimitService, ) { const frontendConfig: FrontendConfig = configService.getOrThrow('FRONTEND'); const keycloakConfig: KeycloakConfig = configService.getOrThrow('KEYCLOAK'); diff --git a/src/modules/person/domain/person-time-limit-info.service.ts b/src/modules/person/domain/person-time-limit-info.service.ts index 8fd075792..63e19bd2f 100644 --- a/src/modules/person/domain/person-time-limit-info.service.ts +++ b/src/modules/person/domain/person-time-limit-info.service.ts @@ -27,6 +27,7 @@ export default class PersonTimeLimitService { const earliestKopersKontext: Personenkontext = kopersKontexte.reduce( (prev: Personenkontext, current: Personenkontext) => prev.createdAt < current.createdAt ? prev : current, + kopersKontexte[0]!, ); const kopersdeadline: Date = new Date(earliestKopersKontext.createdAt); kopersdeadline.setDate(kopersdeadline.getDate() + KOPERS_DEADLINE_IN_DAYS);