Skip to content

Commit

Permalink
SPSH-696: Refactored according to DDD rules by moving logic frpm cont…
Browse files Browse the repository at this point in the history
…roller & aggregate into the repository.
  • Loading branch information
phaelcg committed Jul 16, 2024
1 parent 5a73730 commit ba96bae
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 112 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -831,4 +831,22 @@ describe('dbiam Personenkontext Repo', () => {
expect(result).toEqual(new MismatchedRevisionError('Personenkontext'));
});
});

describe('isRolleAlreadyAssigned', () => {
it('should return true if there is any personenkontext for a rolle', async () => {
const person: Person<true> = await createPerson();
const rolle: Rolle<true> = await rolleRepo.save(DoFactory.createRolle(false));

await sut.save(createPersonenkontext(false, { rolleId: rolle.id, personId: person.id }));
const result: boolean = await sut.isRolleAlreadyAssigned(rolle.id);

expect(result).toBeTruthy();
});

it('should return false if there is no personenkontext for a rolle', async () => {
const result: boolean = await sut.isRolleAlreadyAssigned(faker.string.uuid());

expect(result).toBeFalsy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,8 @@ export class DBiamPersonenkontextRepo {
rolleId: rolleId,
});
}

public async isRolleAlreadyAssigned(id: RolleID): Promise<boolean> {
return (await this.findByRolle(id)).length > 0;
}
}
25 changes: 8 additions & 17 deletions src/modules/rolle/api/rolle.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,31 +315,22 @@ export class RolleController {
@Body() params: UpdateRolleBodyParams,
@Permissions() permissions: PersonPermissions,
): Promise<RolleWithServiceProvidersResponse> {
if (
params.merkmale.length > 0 &&
(await this.dBiamPersonenkontextRepo.isRolleAlreadyAssigned(findRolleByIdParams.rolleId))
) {
throw new UpdateMerkmaleError();
}

//Due to circular reference error, the rolleRepo needs to be passed into the aggregate.
const updatedRolle: Rolle<true> | DomainError = await this.rolleFactory.update(
const result: Rolle<true> | DomainError = await this.rolleRepo.updateRolle(
findRolleByIdParams.rolleId,
params.name,
params.merkmale,
params.systemrechte,
params.serviceProviderIds,
permissions,
);

if (updatedRolle instanceof DomainError) {
throw SchulConnexErrorMapper.mapSchulConnexErrorToHttpException(
SchulConnexErrorMapper.mapDomainErrorToSchulConnexError(updatedRolle),
);
}
//The check is here because it cannot be implemented in the aggregate itself in the method update
//using DBiamPersonenkontextRepo causes circular reference error.
if (
params.merkmale.length > 0 &&
(await updatedRolle.isAlreadyAssigned(this.dBiamPersonenkontextRepo, updatedRolle.id))
) {
throw new UpdateMerkmaleError();
}

const result: Rolle<true> | DomainError = await this.rolleRepo.saveAuthorized(updatedRolle, permissions);
if (result instanceof DomainError) {
throw SchulConnexErrorMapper.mapSchulConnexErrorToHttpException(
SchulConnexErrorMapper.mapDomainErrorToSchulConnexError(result),
Expand Down
1 change: 0 additions & 1 deletion src/modules/rolle/domain/rolle.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Rolle } from './rolle.js';
import { RollenArt, RollenMerkmal, RollenSystemRecht } from './rolle.enums.js';
import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js';
import { DomainError } from '../../../shared/error/domain.error.js';
import { RolleRepo } from '../repo/rolle.repo.js';

@Injectable()
export class RolleFactory {
Expand Down
63 changes: 5 additions & 58 deletions src/modules/rolle/domain/rolle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,14 @@ import { OrganisationRepository } from '../../organisation/persistence/organisat
import { Organisation } from '../../organisation/domain/organisation.js';
import { DBiamPersonenkontextRepo } from '../../personenkontext/persistence/dbiam-personenkontext.repo.js';
import { PersonenkontextFactory } from '../../personenkontext/domain/personenkontext.factory.js';
import { Personenkontext } from '../../personenkontext/domain/personenkontext.js';
import { PersonRepository } from '../../person/persistence/person.repository.js';

describe('Rolle Aggregate', () => {
let module: TestingModule;
let rolleFactory: RolleFactory;
let serviceProviderRepoMock: DeepMocked<ServiceProviderRepo>;
let organisationRepo: DeepMocked<OrganisationRepository>;
let rolleRepoMock: DeepMocked<RolleRepo>;
let dBiamPersonenkontextRepoMock: DeepMocked<DBiamPersonenkontextRepo>;
let personenkontextFactory: PersonenkontextFactory;

function createPersonenkontext<WasPersisted extends boolean>(
this: void,
withId: WasPersisted,
params: Partial<Personenkontext<boolean>> = {},
): Personenkontext<WasPersisted> {
const personenkontext: Personenkontext<WasPersisted> = personenkontextFactory.construct<boolean>(
withId ? faker.string.uuid() : undefined,
withId ? faker.date.past() : undefined,
withId ? faker.date.recent() : undefined,
faker.string.uuid(),
faker.string.uuid(),
faker.string.uuid(),
);

Object.assign(personenkontext, params);

return personenkontext;
}


beforeAll(async () => {
module = await Test.createTestingModule({
Expand Down Expand Up @@ -74,9 +52,6 @@ describe('Rolle Aggregate', () => {
rolleFactory = module.get(RolleFactory);
serviceProviderRepoMock = module.get(ServiceProviderRepo);
organisationRepo = module.get(OrganisationRepository);
rolleRepoMock = module.get(RolleRepo);
dBiamPersonenkontextRepoMock = module.get(DBiamPersonenkontextRepo);
personenkontextFactory = module.get(PersonenkontextFactory);
});

afterAll(async () => {
Expand Down Expand Up @@ -311,44 +286,16 @@ describe('Rolle Aggregate', () => {
});
});

describe('IsAlreadyAssigned', () => {
it('should return false if rolle is not assigned yet', async () => {
const rolle: Rolle<true> = DoFactory.createRolle(true);
dBiamPersonenkontextRepoMock.findByRolle.mockResolvedValueOnce([]);
const result: boolean = await rolle.isAlreadyAssigned(dBiamPersonenkontextRepoMock, rolle.id);
expect(result).toBeFalsy();
});

it('should return true if rolle is already assigned', async () => {
const rolle: Rolle<true> = DoFactory.createRolle(true);
const personenkontext: Personenkontext<true> = createPersonenkontext(true);
dBiamPersonenkontextRepoMock.findByRolle.mockResolvedValueOnce([personenkontext]);
const result: boolean = await rolle.isAlreadyAssigned(dBiamPersonenkontextRepoMock, rolle.id);
expect(result).toBeTruthy();
});
});

describe('update', () => {
it('should return domain error if rolle is does not exist', async () => {
rolleRepoMock.findById.mockResolvedValueOnce(undefined);
const result: Rolle<true> | DomainError = await rolleFactory.update(
rolleRepoMock,
faker.string.uuid(),
'newName',
[],
[],
[],
);
expect(result).toBeInstanceOf(DomainError);
});

it('should return domain error if service provider is does not exist', async () => {
rolleRepoMock.findById.mockResolvedValueOnce(DoFactory.createRolle(true));
serviceProviderRepoMock.findById.mockResolvedValue(undefined);
const result: Rolle<true> | DomainError = await rolleFactory.update(
rolleRepoMock,
faker.string.uuid(),
faker.datatype.datetime(),
faker.datatype.datetime(),
'newName',
faker.string.uuid(),
faker.helpers.enumValue(RollenArt),
[faker.helpers.enumValue(RollenMerkmal)],
[faker.helpers.enumValue(RollenSystemRecht)],
[faker.string.uuid()],
Expand Down
9 changes: 0 additions & 9 deletions src/modules/rolle/domain/rolle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { EntityAlreadyExistsError, EntityNotFoundError } from '../../../shared/e
import { OrganisationID } from '../../../shared/types/aggregate-ids.types.js';
import { OrganisationRepository } from '../../organisation/persistence/organisation.repository.js';
import { Organisation } from '../../organisation/domain/organisation.js';
import { DBiamPersonenkontextRepo } from '../../personenkontext/persistence/dbiam-personenkontext.repo.js';
import { RolleRepo } from '../repo/rolle.repo.js';

export class Rolle<WasPersisted extends boolean> {
private constructor(
Expand Down Expand Up @@ -122,13 +120,6 @@ export class Rolle<WasPersisted extends boolean> {
return !!childOrgas.find((orga: Organisation<true>) => orga.id === orgaId);
}

public async isAlreadyAssigned(
dBiamPersonenkontextRepo: DBiamPersonenkontextRepo,
rolleId: string,
): Promise<boolean> {
return (await dBiamPersonenkontextRepo.findByRolle(rolleId)).length > 0;
}

public addMerkmal(merkmal: RollenMerkmal): void {
if (!this.merkmale.includes(merkmal)) {
this.merkmale.push(merkmal);
Expand Down
41 changes: 16 additions & 25 deletions src/modules/rolle/repo/rolle.repo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ describe('RolleRepo', () => {
let orm: MikroORM;
let em: EntityManager;
let serviceProviderRepo: ServiceProviderRepo;
let rolleFactory: RolleFactory;

beforeAll(async () => {
module = await Test.createTestingModule({
Expand All @@ -40,7 +39,6 @@ describe('RolleRepo', () => {
orm = module.get(MikroORM);
em = module.get(EntityManager);
serviceProviderRepo = module.get(ServiceProviderRepo);
rolleFactory = module.get(RolleFactory);

await DatabaseTestModule.setupDatabase(orm);
}, DEFAULT_TIMEOUT_FOR_TESTCONTAINERS);
Expand Down Expand Up @@ -255,36 +253,34 @@ describe('RolleRepo', () => {
});
});

describe('saveAuthorized', () => {
it('should return the rolle', async () => {
describe('updateRolle', () => {
it('should return the updated rolle', async () => {
const organisationId: OrganisationID = faker.string.uuid();
const rolle: Rolle<true> = await sut.save(
DoFactory.createRolle(false, { administeredBySchulstrukturknoten: organisationId }),
);
const permissions: DeepMocked<PersonPermissions> = createMock<PersonPermissions>();
const newName: string = 'updatedrolle';
const newMermale: RollenMerkmal[] = [RollenMerkmal.KOPERS_PFLICHT];
const newSystemrechte: RollenSystemRecht[] = [RollenSystemRecht.PERSONEN_SOFORT_LOESCHEN];
permissions.getOrgIdsWithSystemrecht.mockResolvedValueOnce([organisationId]);
const updatedRolle: Rolle<true> | DomainError = await rolleFactory.update(
sut,

const rolleResult: Rolle<true> | DomainError = await sut.updateRolle(
rolle.id,
newName,
[RollenMerkmal.KOPERS_PFLICHT],
[RollenSystemRecht.PERSONEN_SOFORT_LOESCHEN],
newMermale,
newSystemrechte,
[],
permissions,
);

if (updatedRolle instanceof DomainError) {
return;
}
const rolleResult: Rolle<true> | DomainError = await sut.saveAuthorized(updatedRolle, permissions);
if (rolleResult instanceof DomainError) {
return;
}
expect(rolleResult.id).toBe(updatedRolle.id);
expect(rolleResult.id).toBe(rolle.id);
expect(rolleResult.name).toBe(newName);
expect(rolleResult.merkmale).toMatchObject(updatedRolle.merkmale);
expect(rolleResult.systemrechte).toMatchObject(updatedRolle.systemrechte);
expect(rolleResult.serviceProviderIds).toMatchObject(updatedRolle.serviceProviderIds);
expect(rolleResult.merkmale).toMatchObject(newMermale);
expect(rolleResult.systemrechte).toMatchObject(newSystemrechte);
expect(rolleResult.serviceProviderIds).toMatchObject([]);
});

it('should return error when permissions are insufficient', async () => {
Expand All @@ -295,20 +291,15 @@ describe('RolleRepo', () => {

permissions.getOrgIdsWithSystemrecht.mockResolvedValueOnce([]);

const updatedRolle: Rolle<true> | DomainError = await rolleFactory.update(
sut,
const rolleResult: Rolle<true> | DomainError = await sut.updateRolle(
rolle.id,
'newName',
faker.company.name(),
[],
[],
[],
permissions,
);

if (updatedRolle instanceof DomainError) {
return;
}
const rolleResult: Rolle<true> | DomainError = await sut.saveAuthorized(updatedRolle, permissions);

expect(rolleResult).toBeInstanceOf(DomainError);
});
});
Expand Down
18 changes: 16 additions & 2 deletions src/modules/rolle/repo/rolle.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,22 @@ export class RolleRepo {
return authorizedRole.error;
}

//Update fields with aggregate
const result: Rolle<true> = await this.update(rolle);
const updatedRolle: Rolle<true> | DomainError = await this.rolleFactory.update(
id,
authorizedRole.value.createdAt,
authorizedRole.value.updatedAt,
name,
authorizedRole.value.administeredBySchulstrukturknoten,
authorizedRole.value.rollenart,
merkmale,
systemrechte,
serviceProviderIds,
);

if (updatedRolle instanceof DomainError) {
return updatedRolle;
}
const result: Rolle<true> = await this.update(updatedRolle);
return result;
}

Expand Down

0 comments on commit ba96bae

Please sign in to comment.