Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPSH-1116 #680

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export class DbiamPersonenkontextWorkflowController {
type: PersonenkontexteUpdateResponse,
})
@ApiBadRequestResponse({
description: 'The personenkontexte could not be updated, may due to unsatisfied specifications.',
description: 'The personenkontexte could not be updated, may be due to unsatisfied specifications.',
type: DbiamPersonenkontexteUpdateError,
})
@ApiConflictResponse({ description: 'Changes are conflicting with current state of personenkontexte.' })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DeepMocked, createMock } from '@golevelup/ts-jest';
// eslint-disable-next-line max-classes-per-file
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
import { DBiamPersonenkontextRepo } from '../persistence/dbiam-personenkontext.repo.js';
import { Personenkontext } from './personenkontext.js';
Expand All @@ -11,6 +12,11 @@ import { DBiamPersonenkontextService } from './dbiam-personenkontext.service.js'
import { DoFactory } from '../../../../test/utils/do-factory.js';
import { RollenArt, RollenMerkmal } from '../../rolle/domain/rolle.enums.js';
import { Rolle } from '../../rolle/domain/rolle.js';
import { RolleModule } from '../../rolle/rolle.module.js';
import { Module } from '@nestjs/common';
import { OrganisationModule } from '../../organisation/organisation.module.js';
import { PersonenkontextPersistenceModule } from '../persistence/PersonenkontextPersistenceModule.js';
import { PersonenkontextSpecificationsModule } from '../specification/personenkontext-specification.module.js';

describe('DBiamPersonenkontextService', () => {
let module: TestingModule;
Expand All @@ -21,28 +27,56 @@ describe('DBiamPersonenkontextService', () => {
let personenkontextFactory: PersonenkontextFactory;

beforeAll(async () => {
module = await Test.createTestingModule({
@Module({
providers: [
DBiamPersonenkontextService,
PersonenkontextFactory,
{
provide: PersonRepository,
useValue: createMock<PersonRepository>(),
},
{
provide: RolleRepo,
useValue: createMock<RolleRepo>(),
},
],
exports: [RolleRepo],
})
class RolleTestModule {}

@Module({
providers: [
{
provide: OrganisationRepository,
useValue: createMock<OrganisationRepository>(),
},
],
exports: [OrganisationRepository],
})
class OrganisationTestModule {}

@Module({
imports: [OrganisationModule, RolleModule],
providers: [
{
provide: DBiamPersonenkontextRepo,
useValue: createMock<DBiamPersonenkontextRepo>(),
},
{
provide: PersonRepository,
useValue: createMock<PersonRepository>(),
},
PersonenkontextFactory,
],
}).compile();
exports: [DBiamPersonenkontextRepo, PersonenkontextFactory, PersonRepository],
})
class PersonenkontextPersistenceTestModule {}

module = await Test.createTestingModule({
imports: [PersonenkontextSpecificationsModule, PersonenkontextPersistenceModule, RolleModule],
providers: [DBiamPersonenkontextService],
})
.overrideModule(RolleModule)
.useModule(RolleTestModule)
.overrideModule(OrganisationModule)
.useModule(OrganisationTestModule)
.overrideModule(PersonenkontextPersistenceModule)
.useModule(PersonenkontextPersistenceTestModule)
.compile();
sut = module.get(DBiamPersonenkontextService);
dbiamPersonenKontextRepoMock = module.get(DBiamPersonenkontextRepo);
rolleRepoMock = module.get(RolleRepo);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,24 @@
import { Injectable } from '@nestjs/common';
import { DBiamPersonenkontextRepo } from '../persistence/dbiam-personenkontext.repo.js';
import { Personenkontext } from './personenkontext.js';
import { NurLehrUndLernAnKlasse } from '../specification/nur-lehr-und-lern-an-klasse.js';
import { GleicheRolleAnKlasseWieSchule } from '../specification/gleiche-rolle-an-klasse-wie-schule.js';
import { PersonenkontextKlasseSpecification } from '../specification/personenkontext-klasse-specification.js';
import { RolleRepo } from '../../rolle/repo/rolle.repo.js';
import { PersonenkontextSpecificationError } from '../specification/error/personenkontext-specification.error.js';
import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js';
import { CheckRollenartSpecification } from '../specification/nur-gleiche-rolle.js';
import { CheckBefristungSpecification } from '../specification/befristung-required-bei-rolle-befristungspflicht.js';
import { Rolle } from '../../rolle/domain/rolle.js';
import { RollenMerkmal } from '../../rolle/domain/rolle.enums.js';

@Injectable()
export class DBiamPersonenkontextService {
public constructor(
private readonly dBiamPersonenkontextRepo: DBiamPersonenkontextRepo,
private readonly organisationRepository: OrganisationRepository,
private readonly rolleRepo: RolleRepo,
private readonly personenkontextKlasseSpecification: PersonenkontextKlasseSpecification,
) {}

public async checkSpecifications(
personenkontext: Personenkontext<false>,
): Promise<Option<PersonenkontextSpecificationError>> {
//Check that only teachers and students are added to classes.
const nurLehrUndLernAnKlasse: NurLehrUndLernAnKlasse = new NurLehrUndLernAnKlasse(
this.organisationRepository,
this.rolleRepo,
);
//Check that person has same role on parent-organisation, if organisation is a class.
const gleicheRolleAnKlasseWieSchule: GleicheRolleAnKlasseWieSchule = new GleicheRolleAnKlasseWieSchule(
this.organisationRepository,
this.dBiamPersonenkontextRepo,
this.rolleRepo,
);

// Checks that the sent personnekontext is of type LERN
//(Only returns an error if the person has some kontext of type LERN already and the sent PK isn't)
const nurRollenartLern: CheckRollenartSpecification = new CheckRollenartSpecification(
this.dBiamPersonenkontextRepo,
this.rolleRepo,
);

// Checks if the sent kontext has a Rolle that is Befristungspflicht. If yes and there is no befristung set then throw an exception
const befristungRequired: CheckBefristungSpecification = new CheckBefristungSpecification(this.rolleRepo);

const pkKlasseSpecification: PersonenkontextKlasseSpecification = new PersonenkontextKlasseSpecification(
nurLehrUndLernAnKlasse,
gleicheRolleAnKlasseWieSchule,
nurRollenartLern,
befristungRequired,
);

return pkKlasseSpecification.returnsError(personenkontext);
return this.personenkontextKlasseSpecification.returnsError(personenkontext);
}

public async isPersonalnummerRequiredForAnyPersonenkontextForPerson(personId: string): Promise<boolean> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ describe('PersonenkontexteUpdate', () => {
rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollenExisting);
rolleRepoMock.findByIds.mockResolvedValueOnce(mapRollen);

jest.spyOn(CheckBefristungSpecification.prototype, 'checkBefristung').mockResolvedValue(true);
jest.spyOn(CheckBefristungSpecification.prototype, 'isSatisfiedBy').mockResolvedValue(true);

const updateError: Personenkontext<true>[] | PersonenkontexteUpdateError = await sut.update();

Expand Down
4 changes: 2 additions & 2 deletions src/modules/personenkontext/domain/personenkontexte-update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ export class PersonenkontexteUpdate {
const isSatisfied: boolean = await new CheckRollenartSpecification(
this.dBiamPersonenkontextRepo,
this.rolleRepo,
).checkRollenart(sentPKs);
).isSatisfiedBy(sentPKs);

if (!isSatisfied) {
return new UpdateInvalidRollenartForLernError();
Expand All @@ -286,7 +286,7 @@ export class PersonenkontexteUpdate {
private async checkBefristungSpecification(
sentPKs: Personenkontext<boolean>[],
): Promise<Option<PersonenkontexteUpdateError>> {
const isSatisfied: boolean = await new CheckBefristungSpecification(this.rolleRepo).checkBefristung(sentPKs);
const isSatisfied: boolean = await new CheckBefristungSpecification(this.rolleRepo).isSatisfiedBy(sentPKs);

if (!isSatisfied) {
return new PersonenkontextBefristungRequiredError();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Module } from '@nestjs/common';
import { DBiamPersonenkontextRepo } from './dbiam-personenkontext.repo.js';
import { PersonenkontextRepo } from './personenkontext.repo.js';
import { DBiamPersonenkontextRepoInternal } from './internal-dbiam-personenkontext.repo.js';
import { PersonenkontextFactory } from '../domain/personenkontext.factory.js';
import { PersonModule } from '../../person/person.module.js';
import { OrganisationModule } from '../../organisation/organisation.module.js';
import { RolleModule } from '../../rolle/rolle.module.js';

@Module({
imports: [PersonModule, OrganisationModule, RolleModule],
providers: [
DBiamPersonenkontextRepo,
PersonenkontextRepo,
DBiamPersonenkontextRepoInternal,
PersonenkontextFactory,
],
exports: [DBiamPersonenkontextRepo, PersonenkontextRepo, DBiamPersonenkontextRepoInternal, PersonenkontextFactory],
})
export class PersonenkontextPersistenceModule {}
18 changes: 10 additions & 8 deletions src/modules/personenkontext/personenkontext.module.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { 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';
import { PersonModule } from '../person/person.module.js';
import { DBiamPersonenkontextRepo } from './persistence/dbiam-personenkontext.repo.js';
import { RolleModule } from '../rolle/rolle.module.js';
import { OrganisationModule } from '../organisation/organisation.module.js';
import { DBiamPersonenkontextService } from './domain/dbiam-personenkontext.service.js';
import { DbiamPersonenkontextFactory } from './domain/dbiam-personenkontext.factory.js';
import { PersonenkontextFactory } from './domain/personenkontext.factory.js';
import { EventModule } from '../../core/eventbus/index.js';
import { DBiamPersonenkontextRepoInternal } from './persistence/internal-dbiam-personenkontext.repo.js';
import { PersonenkontextPersistenceModule } from './persistence/PersonenkontextPersistenceModule.js';
import { PersonenkontextCreationService } from './domain/personenkontext-creation.service.js';
import { PersonenkontextWorkflowFactory } from './domain/personenkontext-workflow.factory.js';
import { PersonenkontextRepo } from './persistence/personenkontext.repo.js';
import { DBiamPersonenkontextRepo } from './persistence/dbiam-personenkontext.repo.js';
import { DBiamPersonenkontextRepoInternal } from './persistence/internal-dbiam-personenkontext.repo.js';
import { PersonenkontextFactory } from './domain/personenkontext.factory.js';
import { PersonenkontextSpecificationsModule } from './specification/personenkontext-specification.module.js';

@Module({
imports: [
EventModule,
PersonModule,
RolleModule,
OrganisationModule,
PersonenkontextSpecificationsModule,
PersonenkontextPersistenceModule,
LoggerModule.register(PersonenKontextModule.name),
],
providers: [
Expand All @@ -35,12 +39,10 @@ import { PersonenkontextWorkflowFactory } from './domain/personenkontext-workflo
],
exports: [
PersonenkontextService,
PersonenkontextRepo,
DBiamPersonenkontextService,
DBiamPersonenkontextRepo,
DbiamPersonenkontextFactory,
DBiamPersonenkontextRepoInternal, // TODO: Needed by seeding
PersonenkontextFactory,
PersonenkontextPersistenceModule,
PersonenkontextSpecificationsModule,
PersonenkontextCreationService,
PersonenkontextWorkflowFactory,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ import { RolleRepo } from '../../rolle/repo/rolle.repo.js';
import { RollenMerkmal } from '../../rolle/domain/rolle.enums.js';
import { Personenkontext } from '../domain/personenkontext.js';
import { Rolle } from '../../rolle/domain/rolle.js';
import { Injectable } from '@nestjs/common';
import { CompositeSpecification } from '../../specification/specifications.js';

export class CheckBefristungSpecification {
public constructor(private readonly rolleRepo: RolleRepo) {}
@Injectable()
export class CheckBefristungSpecification extends CompositeSpecification<Personenkontext<boolean>[]> {
public constructor(private readonly rolleRepo: RolleRepo) {
super();
}

public async checkBefristung(sentPKs: Personenkontext<boolean>[]): Promise<boolean> {
public async isSatisfiedBy(sentPKs: Personenkontext<boolean>[]): Promise<boolean> {
// Early return if all Personenkontext have befristung defined
if (sentPKs.every((pk: Personenkontext<boolean>) => pk.befristung !== undefined)) {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import { Rolle } from '../../rolle/domain/rolle.js';
import { DBiamPersonenkontextRepo } from '../persistence/dbiam-personenkontext.repo.js';
import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js';
import { Organisation } from '../../organisation/domain/organisation.js';
import { Injectable } from '@nestjs/common';

/**
* Only needs to be checked when referenced organisation is of type KLASSE.
* Used to check, that a person already owns identical rolle at schule, when creating Personenkontext
* for that person with a rolle on a klasse.
*/
@Injectable()
export class GleicheRolleAnKlasseWieSchule extends CompositeSpecification<Personenkontext<boolean>> {
public constructor(
private readonly organisationRepo: OrganisationRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import { RollenArt } from '../../rolle/domain/rolle.enums.js';
import { Personenkontext } from '../domain/personenkontext.js';
import { Rolle } from '../../rolle/domain/rolle.js';
import { ExistingRolleUndefined } from '../domain/error/existing-rolle-undefined.error.js';
import { CompositeSpecification } from '../../specification/specifications.js';

export class CheckRollenartSpecification {
export class CheckRollenartSpecification extends CompositeSpecification<Personenkontext<boolean>[]> {
public constructor(
private readonly personenkontextRepo: DBiamPersonenkontextRepo,
private readonly rolleRepo: RolleRepo,
) {}
) {
super();
}

public async checkRollenart(sentPKs: Personenkontext<boolean>[]): Promise<boolean> {
public async isSatisfiedBy(sentPKs: Personenkontext<boolean>[]): Promise<boolean> {
// Fetch all personenkontexte for the person
let existingPKs: Personenkontext<true>[] = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { Rolle } from '../../rolle/domain/rolle.js';
import { RollenArt } from '../../rolle/domain/rolle.enums.js';
import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js';
import { Organisation } from '../../organisation/domain/organisation.js';
import { Injectable } from '@nestjs/common';

/**
* Only needs to be checked when referenced organisation is of type KLASSE.
*/
@Injectable()
export class NurLehrUndLernAnKlasse extends CompositeSpecification<Personenkontext<boolean>> {
public constructor(
private readonly organisationRepository: OrganisationRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ import { OrganisationsTyp } from '../../organisation/domain/organisation.enums.j
import { Rolle } from '../../rolle/domain/rolle.js';
import { RollenArt } from '../../rolle/domain/rolle.enums.js';
import { Organisation } from '../../organisation/domain/organisation.js';
import { Injectable } from '@nestjs/common';

/**
* Only needs to be checked when referenced organisation is of type KLASSE.
* Needs to be refactored into a specification
* Checks if the organisation matches the rollenart.
*
* Sysadmins can only exist on LAND or ROOT organisations.
* LEIT can only exist on Schools.
* LERN (students) only exist on forms
* LEHR (teachers) only exist on schools.
*/

@Injectable()
export class OrganisationMatchesRollenart {
public isSatisfiedBy(organisation: Organisation<true>, rolle: Rolle<true>): boolean {
if (rolle.rollenart === RollenArt.SYSADMIN)
Expand Down
Loading
Loading