Skip to content

Commit

Permalink
Vouchers managment (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmarchois authored Apr 4, 2021
1 parent a34961e commit c7fcf08
Show file tree
Hide file tree
Showing 62 changed files with 1,146 additions and 276 deletions.
16 changes: 16 additions & 0 deletions api/migrations/1617438352492-Voucher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {MigrationInterface, QueryRunner} from "typeorm";

export class Voucher1617438352492 implements MigrationInterface {
name = 'Voucher1617438352492'

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "voucher" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "email" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "schoolId" uuid NOT NULL, CONSTRAINT "PK_677ae75f380e81c2f103a57ffaf" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "voucher" ADD CONSTRAINT "FK_c837a392c774db4d54bc8d8484c" FOREIGN KEY ("schoolId") REFERENCES "school"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "voucher" DROP CONSTRAINT "FK_c837a392c774db4d54bc8d8484c"`);
await queryRunner.query(`DROP TABLE "voucher"`);
}

}
3 changes: 3 additions & 0 deletions api/src/Application/ICodeGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface ICodeGenerator {
generate(): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ICommand } from 'src/Application/ICommand';
import { User } from 'src/Domain/User/User.entity';

export class AddUserToSchoolCommand implements ICommand {
constructor(
public readonly user: User,
public readonly schoolId: string
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { mock, instance, when, verify, anything, deepEqual } from 'ts-mockito';
import { School } from 'src/Domain/School/School.entity';
import { AddUserToSchoolCommandHandler } from 'src/Application/School/Command/User/AddUserToSchoolCommandHandler';
import { AddUserToSchoolCommand } from 'src/Application/School/Command/User/AddUserToSchoolCommand';
import { SchoolRepository } from 'src/Infrastructure/School/Repository/SchoolRepository';
import { User, UserRole } from 'src/Domain/User/User.entity';
import { SchoolNotFoundException } from 'src/Domain/School/Exception/SchoolNotFoundException';
import { SchoolUserRepository } from 'src/Infrastructure/School/Repository/SchoolUserRepository';
import { SchoolUser } from 'src/Domain/School/SchoolUser.entity';
import { IsUserAlreadyAddedToSchool } from 'src/Domain/User/Specification/IsUserAlreadyAddedToSchool';
import { UserAlreadyAddedToSchoolException } from 'src/Domain/User/Exception/UserAlreadyAddedToSchoolException';

describe('AddUserToSchoolCommandHandler', () => {
let schoolRepository: SchoolRepository;
let schoolUserRepository: SchoolUserRepository;
let isUserAlreadyAddedToSchool: IsUserAlreadyAddedToSchool;
let school: School;
let handler: AddUserToSchoolCommandHandler;

const user = mock(User);
const createdSchoolUser = mock(SchoolUser);

const command = new AddUserToSchoolCommand(
instance(user),
'fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f',
);

beforeEach(() => {
schoolRepository = mock(SchoolRepository);
schoolUserRepository = mock(SchoolUserRepository);
isUserAlreadyAddedToSchool = mock(IsUserAlreadyAddedToSchool);
school = mock(School);

handler = new AddUserToSchoolCommandHandler(
instance(schoolRepository),
instance(schoolUserRepository),
instance(isUserAlreadyAddedToSchool),
);
});

it('testDirectorSuccessfullyAdded', async () => {
when(createdSchoolUser.getId()).thenReturn('0b1d9435-4258-42f1-882d-4f314f8fb57d');
when(user.getRole()).thenReturn(UserRole.DIRECTOR);
when(schoolRepository.findOneById('fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f'))
.thenResolve(instance(school));
when(isUserAlreadyAddedToSchool.isSatisfiedBy(instance(school), instance(user)))
.thenResolve(false);
when(schoolUserRepository.save(
deepEqual(new SchoolUser(instance(school), instance(user)))
)).thenResolve(instance(createdSchoolUser));

expect(await handler.execute(command)).toBe(
'0b1d9435-4258-42f1-882d-4f314f8fb57d'
);

verify(schoolRepository.findOneById('fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f')).once();
verify(isUserAlreadyAddedToSchool.isSatisfiedBy(instance(school), instance(user))).once();
verify(schoolUserRepository.save(
deepEqual(new SchoolUser(instance(school), instance(user)))
)).once();
});

it('testSchoolNotFound', async () => {
when(schoolRepository.findOneById('fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f'))
.thenResolve(null);
try {
expect(await handler.execute(command)).toBeUndefined();
} catch (e) {
expect(e).toBeInstanceOf(SchoolNotFoundException);
expect(e.message).toBe('schools.errors.not_found');
verify(schoolRepository.findOneById('fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f')).once();
verify(schoolUserRepository.save(anything())).never();
verify(isUserAlreadyAddedToSchool.isSatisfiedBy(anything(), anything())).never();
}
});

it('testUserAlreadyAdded', async () => {
when(schoolRepository.findOneById('fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f'))
.thenResolve(instance(school));
when(isUserAlreadyAddedToSchool.isSatisfiedBy(instance(school), instance(user)))
.thenResolve(true);

try {
expect(await handler.execute(command)).toBeUndefined();
} catch (e) {
expect(e).toBeInstanceOf(UserAlreadyAddedToSchoolException);
expect(e.message).toBe('users.errors.already_assigned_to_school');
verify(schoolRepository.findOneById('fcf9a99f-0c7b-45ca-b68a-bfd79d73a49f')).once();
verify(schoolUserRepository.save(anything())).never();
verify(isUserAlreadyAddedToSchool.isSatisfiedBy(instance(school), instance(user))).once();
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Inject } from '@nestjs/common';
import { CommandHandler } from '@nestjs/cqrs';
import { SchoolNotFoundException } from 'src/Domain/School/Exception/SchoolNotFoundException';
import { ISchoolRepository } from 'src/Domain/School/Repository/ISchoolRepository';
import { ISchoolUserRepository } from 'src/Domain/School/Repository/ISchoolUserRepository';
import { SchoolUser } from 'src/Domain/School/SchoolUser.entity';
import { UserAlreadyAddedToSchoolException } from 'src/Domain/User/Exception/UserAlreadyAddedToSchoolException';
import { IsUserAlreadyAddedToSchool } from 'src/Domain/User/Specification/IsUserAlreadyAddedToSchool';
import { AddUserToSchoolCommand } from './AddUserToSchoolCommand';

@CommandHandler(AddUserToSchoolCommand)
export class AddUserToSchoolCommandHandler {
constructor(
@Inject('ISchoolRepository')
private readonly schoolRepository: ISchoolRepository,
@Inject('ISchoolUserRepository')
private readonly schoolUserRepository: ISchoolUserRepository,
private readonly isUserAlreadyAddedToSchool: IsUserAlreadyAddedToSchool,
) {}

public async execute(command: AddUserToSchoolCommand): Promise<string> {
const { schoolId, user } = command;

const school = await this.schoolRepository.findOneById(schoolId);
if (!school) {
throw new SchoolNotFoundException();
}

if (true === (await this.isUserAlreadyAddedToSchool.isSatisfiedBy(school, user))) {
throw new UserAlreadyAddedToSchoolException();
}

const schoolUser = await this.schoolUserRepository.save(
new SchoolUser(school, user)
);

// @todo : send email

return schoolUser.getId();
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ICommand } from 'src/Application/ICommand';

export class RemoveSchoolUserCommand implements ICommand {
constructor(public readonly id: string) {}
}
Loading

0 comments on commit c7fcf08

Please sign in to comment.