Skip to content

Commit

Permalink
BC-5102 - use type guards
Browse files Browse the repository at this point in the history
  • Loading branch information
virgilchiriac committed Oct 2, 2023
1 parent f503620 commit bb79756
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 60 deletions.
14 changes: 9 additions & 5 deletions apps/server/src/modules/board/service/submission-item.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ObjectId } from 'bson';
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, NotFoundException, UnprocessableEntityException } from '@nestjs/common';

import { EntityId, SubmissionContainerElement, SubmissionItem } from '@shared/domain';
import { EntityId, isSubmissionContainerElement, SubmissionContainerElement, SubmissionItem } from '@shared/domain';
import { ValidationError } from '@shared/common';

import { BoardDoRepo } from '../repo';
Expand Down Expand Up @@ -42,13 +42,17 @@ export class SubmissionItemService {
}

async update(submissionItem: SubmissionItem, completed: boolean): Promise<void> {
const parent = (await this.boardDoRepo.findParentOfId(submissionItem.id)) as SubmissionContainerElement;
const submissionContainterElement = await this.boardDoRepo.findParentOfId(submissionItem.id);
if (!isSubmissionContainerElement(submissionContainterElement)) {
throw new UnprocessableEntityException();
}

const now = new Date();
if (parent.dueDate && parent.dueDate < now) {
if (submissionContainterElement.dueDate && submissionContainterElement.dueDate < now) {
throw new ValidationError('not allowed to save anymore');
}
submissionItem.completed = completed;

await this.boardDoRepo.save(submissionItem, parent);
await this.boardDoRepo.save(submissionItem, submissionContainterElement);
}
}
27 changes: 18 additions & 9 deletions apps/server/src/modules/board/uc/element.uc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { AnyBoardDo, EntityId, SubmissionContainerElement, SubmissionItem, UserRoleEnum } from '@shared/domain';
import {
AnyBoardDo,
EntityId,
isSubmissionContainerElement,
isSubmissionItem,
SubmissionItem,
UserRoleEnum,
} from '@shared/domain';
import { Logger } from '@src/core/logger';
import { AuthorizationService } from '@src/modules/authorization';
import { Action } from '@src/modules/authorization/types/action.enum';
Expand Down Expand Up @@ -33,33 +40,35 @@ export class ElementUc {
contentElementId: EntityId,
completed: boolean
): Promise<SubmissionItem> {
const submissionContainer = (await this.elementService.findById(contentElementId)) as SubmissionContainerElement;
const submissionContainerElement = await this.elementService.findById(contentElementId);

if (!(submissionContainer instanceof SubmissionContainerElement))
if (!isSubmissionContainerElement(submissionContainerElement)) {
throw new HttpException(
'Cannot create submission-item for non submission-container-element',
HttpStatus.UNPROCESSABLE_ENTITY
);
}

if (!submissionContainer.children.every((child) => child instanceof SubmissionItem))
if (!submissionContainerElement.children.every((child) => isSubmissionItem(child))) {
throw new HttpException(
'Children of submission-container-element must be of type submission-item',
HttpStatus.UNPROCESSABLE_ENTITY
);
}

const userSubmissionExists = submissionContainer.children.find(
(item) => (item as SubmissionItem).userId === userId
);
const userSubmissionExists = submissionContainerElement.children
.filter(isSubmissionItem)
.find((item) => item.userId === userId);
if (userSubmissionExists) {
throw new HttpException(
'User is not allowed to have multiple submission-items per submission-container-element',
HttpStatus.NOT_ACCEPTABLE
);
}

await this.checkPermission(userId, submissionContainer, Action.read, UserRoleEnum.STUDENT);
await this.checkPermission(userId, submissionContainerElement, Action.read, UserRoleEnum.STUDENT);

const submissionItem = await this.submissionItemService.create(userId, submissionContainer, { completed });
const submissionItem = await this.submissionItemService.create(userId, submissionContainerElement, { completed });

return submissionItem;
}
Expand Down
24 changes: 1 addition & 23 deletions apps/server/src/modules/board/uc/submission-item.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,29 +194,7 @@ describe(SubmissionItemUc.name, () => {
it('should throw HttpException', async () => {
const { teacher, fileEl } = setup();

await expect(uc.findSubmissionItems(teacher.id, fileEl.id)).rejects.toThrow(
'Id does not belong to a submission container'
);
});
});
describe('when called with invalid submission container children', () => {
const setup = () => {
const teacher = userFactory.buildWithId();
const fileEl = fileElementFactory.build();
const submissionContainer = submissionContainerElementFactory.build({
children: [fileEl],
});
elementService.findById.mockResolvedValue(submissionContainer);

return { teacher, submissionContainer };
};

it('should throw HttpException', async () => {
const { teacher, submissionContainer } = setup();

await expect(uc.findSubmissionItems(teacher.id, submissionContainer.id)).rejects.toThrow(
'Children of submission-container-element must be of type submission-item'
);
await expect(uc.findSubmissionItems(teacher.id, fileEl.id)).rejects.toThrow('Id is not submission container');
});
});
});
Expand Down
34 changes: 11 additions & 23 deletions apps/server/src/modules/board/uc/submission-item.uc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { ForbiddenException, forwardRef, HttpException, HttpStatus, Inject, Inje
import {
AnyBoardDo,
EntityId,
isSubmissionContainerElement,
isSubmissionItem,
SubmissionContainerElement,

Check failure on line 7 in apps/server/src/modules/board/uc/submission-item.uc.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'SubmissionContainerElement' is defined but never used
SubmissionItem,
UserBoardRoles,
Expand Down Expand Up @@ -29,22 +31,20 @@ export class SubmissionItemUc {
userId: EntityId,
submissionContainerId: EntityId
): Promise<{ submissionItems: SubmissionItem[]; users: UserBoardRoles[] }> {
const submissionContainer = await this.getSubmissionContainer(submissionContainerId);
await this.checkPermission(userId, submissionContainer, Action.read);
const submissionContainerElement = await this.elementService.findById(submissionContainerId);

let submissionItems = submissionContainer.children as SubmissionItem[];

if (!submissionItems.every((child) => child instanceof SubmissionItem)) {
throw new HttpException(
'Children of submission-container-element must be of type submission-item',
HttpStatus.UNPROCESSABLE_ENTITY
);
if (!isSubmissionContainerElement(submissionContainerElement)) {
throw new HttpException('Id is not submission container', HttpStatus.UNPROCESSABLE_ENTITY);
}

const boardAuthorizable = await this.boardDoAuthorizableService.getBoardAuthorizable(submissionContainer);
await this.checkPermission(userId, submissionContainerElement, Action.read);

let submissionItems = submissionContainerElement.children.filter(isSubmissionItem);

const boardAuthorizable = await this.boardDoAuthorizableService.getBoardAuthorizable(submissionContainerElement);
let users = boardAuthorizable.users.filter((user) => user.userRoleEnum === UserRoleEnum.STUDENT);

const isAuthorizedStudent = await this.isAuthorizedStudent(userId, submissionContainer);
const isAuthorizedStudent = await this.isAuthorizedStudent(userId, submissionContainerElement);
if (isAuthorizedStudent) {
submissionItems = submissionItems.filter((item) => item.userId === userId);
users = [];
Expand Down Expand Up @@ -86,18 +86,6 @@ export class SubmissionItemUc {
return false;
}

private async getSubmissionContainer(submissionContainerId: EntityId): Promise<SubmissionContainerElement> {
const submissionContainer = (await this.elementService.findById(
submissionContainerId
)) as SubmissionContainerElement;

if (!(submissionContainer instanceof SubmissionContainerElement)) {
throw new HttpException('Id does not belong to a submission container', HttpStatus.UNPROCESSABLE_ENTITY);
}

return submissionContainer;
}

private async checkPermission(
userId: EntityId,
boardDo: AnyBoardDo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ export interface SubmissionItemProps extends BoardCompositeProps {
completed: boolean;
userId: EntityId;
}

export function isSubmissionItem(reference: unknown): reference is SubmissionItem {
return reference instanceof SubmissionItem;
}

0 comments on commit bb79756

Please sign in to comment.