Skip to content

Commit

Permalink
Get shooting by id (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmarchois authored Apr 24, 2021
1 parent 2cdb9e1 commit 45e0471
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IQuery } from 'src/Application/IQuery';

export class GetShootingByIdQuery implements IQuery {
constructor(public readonly id: string) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { mock, instance, when, verify } from 'ts-mockito';
import { Shooting, ShootingStatus } from 'src/Domain/School/Shooting.entity';
import { ShootingRepository } from 'src/Infrastructure/School/Repository/ShootingRepository';
import { ShootingView } from '../../View/ShootingView';
import { GetShootingByIdQuery } from './GetSchootingByIdQuery';
import { GetShootingByIdQueryHandler } from './GetShootingByIdQueryHandler';
import { ShootingNotFoundException } from 'src/Domain/School/Exception/ShootingNotFoundException';

describe('GetShootingByIdQueryHandler', () => {
const query = new GetShootingByIdQuery('eb9e1d9b-dce2-48a9-b64f-f0872f3157d2');

it('testGetShooting', async () => {
const shootingRepository = mock(ShootingRepository);
const queryHandler = new GetShootingByIdQueryHandler(instance(shootingRepository));
const expectedResult = new ShootingView(
'eb9e1d9b-dce2-48a9-b64f-f0872f3157d2',
'Prise de vue fin année',
ShootingStatus.DISABLED,
new Date('2021-04-18'),
new Date('2021-09-01')
);

const shooting = mock(Shooting);
when(shooting.getId()).thenReturn('eb9e1d9b-dce2-48a9-b64f-f0872f3157d2');
when(shooting.getName()).thenReturn('Prise de vue fin année');
when(shooting.getClosingDate()).thenReturn(new Date('2021-09-01'));
when(shooting.getShootingDate()).thenReturn(new Date('2021-04-18'));
when(shooting.getStatus()).thenReturn(ShootingStatus.DISABLED);
when(
shootingRepository.findOneById('eb9e1d9b-dce2-48a9-b64f-f0872f3157d2')
).thenResolve(instance(shooting));

expect(await queryHandler.execute(query)).toMatchObject(expectedResult);

verify(
shootingRepository.findOneById('eb9e1d9b-dce2-48a9-b64f-f0872f3157d2')
).once();
});

it('testGetShootingNotFound', async () => {
const shootingRepository = mock(ShootingRepository);
const queryHandler = new GetShootingByIdQueryHandler(instance(shootingRepository));
when(
shootingRepository.findOneById('eb9e1d9b-dce2-48a9-b64f-f0872f3157d2')
).thenResolve(null);

try {
expect(await queryHandler.execute(query)).toBeUndefined();
} catch (e) {
expect(e).toBeInstanceOf(ShootingNotFoundException);
expect(e.message).toBe('schools.shootings.errors.not_found');
verify(
shootingRepository.findOneById('eb9e1d9b-dce2-48a9-b64f-f0872f3157d2')
).once();
}
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { QueryHandler } from '@nestjs/cqrs';
import { Inject } from '@nestjs/common';
import { GetShootingByIdQuery } from './GetSchootingByIdQuery';
import { IShootingRepository } from 'src/Domain/School/Repository/IShootingRepository';
import { ShootingView } from '../../View/ShootingView';
import { ShootingNotFoundException } from 'src/Domain/School/Exception/ShootingNotFoundException';

@QueryHandler(GetShootingByIdQuery)
export class GetShootingByIdQueryHandler {
constructor(
@Inject('IShootingRepository')
private readonly shootingRepository: IShootingRepository
) {}

public async execute(query: GetShootingByIdQuery): Promise<ShootingView> {
const shooting = await this.shootingRepository.findOneById(query.id);

if (!shooting) {
throw new ShootingNotFoundException();
}

return new ShootingView(
shooting.getId(),
shooting.getName(),
shooting.getStatus(),
shooting.getShootingDate(),
shooting.getClosingDate()
);
}
}
5 changes: 5 additions & 0 deletions api/src/Domain/School/Exception/ShootingNotFoundException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class ShootingNotFoundException extends Error {
constructor() {
super('schools.shootings.errors.not_found');
}
}
1 change: 1 addition & 0 deletions api/src/Domain/School/Repository/IShootingRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { Shooting } from '../Shooting.entity';
export interface IShootingRepository {
save(shooting: Shooting): Promise<Shooting>;
findBySchool(schoolId: string): Promise<Shooting[]>;
findOneById(id: string): Promise<Shooting | undefined>;
countBySchool(schoolId: string): Promise<number>;
}
39 changes: 39 additions & 0 deletions api/src/Infrastructure/School/Action/Shooting/GetShootingAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
Controller,
Inject,
UseGuards,
Param,
Get,
NotFoundException
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { IQueryBus } from 'src/Application/IQueryBus';
import { GetShootingByIdQuery } from 'src/Application/School/Query/Shooting/GetSchootingByIdQuery';
import { ShootingView } from 'src/Application/School/View/ShootingView';
import { UserRole } from 'src/Domain/User/User.entity';
import { IdDTO } from 'src/Infrastructure/Common/DTO/IdDTO';
import { Roles } from 'src/Infrastructure/User/Decorator/Roles';
import { RolesGuard } from 'src/Infrastructure/User/Security/RolesGuard';

@Controller('schools/:schoolId/shootings')
@ApiTags('School shooting')
@ApiBearerAuth()
@UseGuards(AuthGuard('bearer'), RolesGuard)
export class GetShootingAction {
constructor(
@Inject('IQueryBus')
private readonly queryBus: IQueryBus
) {}

@Get(':id')
@Roles(UserRole.PHOTOGRAPHER)
@ApiOperation({ summary: 'Get shooting' })
public async index(@Param() { id }: IdDTO): Promise<ShootingView> {
try {
return await this.queryBus.execute(new GetShootingByIdQuery(id));
} catch (e) {
throw new NotFoundException(e.message);
}
}
}
13 changes: 13 additions & 0 deletions api/src/Infrastructure/School/Repository/ShootingRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ export class ShootingRepository implements IShootingRepository {
.getMany();
}

public findOneById(id: string): Promise<Shooting | undefined> {
return this.repository
.createQueryBuilder('shooting')
.select([
'shooting.name',
'shooting.status',
'shooting.shootingDate',
'shooting.closingDate'
])
.where('shooting.id = :id', { id })
.getOne();
}

public countBySchool(id: string): Promise<number> {
return this.repository
.createQueryBuilder('shooting')
Expand Down
6 changes: 5 additions & 1 deletion api/src/Infrastructure/School/school.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ import { CountDiscountsBySchoolQueryHandler } from 'src/Application/School/Query
import { RemoveDiscountAction } from './Action/Discount/RemoveDiscountAction';
import { RemoveDiscountCommandHandler } from 'src/Application/School/Command/Discount/RemoveDiscountCommandHandler';
import { SchoolCreatedEventListener } from 'src/Application/School/EventListener/SchoolCreatedEventListener';
import { GetShootingByIdQueryHandler } from 'src/Application/School/Query/Shooting/GetShootingByIdQueryHandler';
import { GetShootingAction } from './Action/Shooting/GetShootingAction';

@Module({
imports: [
Expand Down Expand Up @@ -107,6 +109,7 @@ import { SchoolCreatedEventListener } from 'src/Application/School/EventListener
ConsumeVoucherAction,
RemoveVoucherAction,
GetSchoolShootingsAction,
GetShootingAction,
CreateShootingAction,
CountSchoolShootingsAction,
GetSchoolDiscountsAction,
Expand Down Expand Up @@ -156,7 +159,8 @@ import { SchoolCreatedEventListener } from 'src/Application/School/EventListener
GetDiscountsBySchoolQueryHandler,
CountDiscountsBySchoolQueryHandler,
RemoveDiscountCommandHandler,
SchoolCreatedEventListener
SchoolCreatedEventListener,
GetShootingByIdQueryHandler
]
})
export class SchoolModule {}
6 changes: 5 additions & 1 deletion client/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@
"add": {
"title": "Nouvelle prise de vue"
},
"errors": {
"not_found": "La prise de vue recherchée n'existe pas."
},
"form": {
"name": "Nom de la prise de vue",
"closing_date": "Date de la fermeture des commandes",
Expand All @@ -312,7 +315,8 @@
"shooting_date": "Date",
"status": "Etat",
"class": "Nb classes",
"photos": "Nb photos"
"photos": "Nb photos",
"orders": "Nb commandes"
}
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<script context="module">
export const preload = ({ params: { id, shootingId } }) => {
return { id, shootingId };
};
</script>

<script>
import { _ } from 'svelte-i18n';
import { onMount } from 'svelte';
import { get } from 'utils/axios';
import Breadcrumb from 'components/Breadcrumb.svelte';
import { errorNormalizer } from 'normalizer/errors';
import ServerErrors from 'components/ServerErrors.svelte';
import H4Title from 'components/H4Title.svelte';
export let id;
export let shootingId;
let title;
let school;
let shooting;
let errors = [];
onMount(async () => {
try {
const [ schoolResponse, shootingResponse ] = await Promise.all([
get(`schools/${id}`),
get(`schools/${id}/shootings/${shootingId}`)
]);
school = schoolResponse.data;
shooting = shootingResponse.data;
title = shooting.name;
} catch (e) {
errors = errorNormalizer(e);
}
});
</script>

<svelte:head>
<title>{title} - {$_('app')}</title>
</svelte:head>

<Breadcrumb items={[
{ title: $_('schools.breadcrumb'), path: '/admin/schools' },
{ title: school?.name, path: `/admin/schools/${id}` },
{ title: $_('schools.shootings.title'), path: `/admin/schools/${id}/shootings` },
{ title }
]} />
<div class="inline-flex items-center">
<H4Title {title} />
</div>
<ServerErrors {errors} />
2 changes: 2 additions & 0 deletions client/src/routes/admin/schools/[id]/shootings/_Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<th class="px-4 py-3">{$_('schools.shootings.list.closing_date')}</th>
<th class="px-4 py-3">{$_('schools.shootings.list.class')}</th>
<th class="px-4 py-3">{$_('schools.shootings.list.photos')}</th>
<th class="px-4 py-3">{$_('schools.shootings.list.orders')}</th>
<th class="px-4 py-3">{$_('schools.shootings.list.status')}</th>
<th class="px-4 py-3">{$_('common.detail')}</th>
</tr>
Expand All @@ -37,6 +38,7 @@
</td>
<td class="px-4 py-3">0</td>
<td class="px-4 py-3">0</td>
<td class="px-4 py-3">0</td>
<td class="px-4 py-3 text-sm">
{#if status === 'enabled'}
<GreenBadge value={$_("schools.shootings.status.enabled")}/>
Expand Down

0 comments on commit 45e0471

Please sign in to comment.