Skip to content

Commit

Permalink
BC-4783 Migrate GET endpoints of school API (#4440)
Browse files Browse the repository at this point in the history
  • Loading branch information
dyedwiper authored Dec 19, 2023
1 parent 7d7818d commit ef05399
Show file tree
Hide file tree
Showing 112 changed files with 2,869 additions and 250 deletions.
2 changes: 2 additions & 0 deletions apps/server/src/modules/authorization/authorization.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LegacySchoolRule,
LessonRule,
SchoolExternalToolRule,
SchoolRule,
SchoolSystemOptionsRule,
SubmissionRule,
SystemRule,
Expand All @@ -38,6 +39,7 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers';
CourseRule,
GroupRule,
LessonRule,
SchoolRule,
SchoolExternalToolRule,
SubmissionRule,
TaskRule,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { BoardDoAuthorizable, BoardRoles } from '@shared/domain/domainobject';
import { User } from '@shared/domain/entity';
import { BoardDoAuthorizable, BoardRoles } from '@shared/domain/domainobject/board/types';
import { User } from '@shared/domain/entity/user.entity';
import { Action, AuthorizationContext, Rule } from '../type';
import { AuthorizationHelper } from '../service/authorization.helper';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export * from './course-group.rule';
export * from './course.rule';
export * from './legacy-school.rule';
export * from './lesson.rule';
export * from './school.rule';
export * from './school-external-tool.rule';
export * from './submission.rule';
export * from './task.rule';
Expand Down
129 changes: 129 additions & 0 deletions apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { schoolFactory } from '@modules/school/testing/school.factory';
import { Test, TestingModule } from '@nestjs/testing';
import { Permission } from '@shared/domain/interface';
import { roleFactory, setupEntities, userFactory } from '@shared/testing';
import { AuthorizationContextBuilder } from '../mapper';
import { AuthorizationHelper } from '../service/authorization.helper';
import { SchoolRule } from './school.rule';

describe('SchoolRule', () => {
let rule: SchoolRule;

beforeAll(async () => {
await setupEntities();

const module: TestingModule = await Test.createTestingModule({
providers: [AuthorizationHelper, SchoolRule],
}).compile();

rule = await module.get(SchoolRule);
});

afterEach(() => {
jest.resetAllMocks();
});

const permissionA = 'a' as Permission;
const permissionB = 'b' as Permission;

const setupSchoolAndUser = () => {
const school = schoolFactory.build();
const role = roleFactory.build({ permissions: [permissionA] });
const user = userFactory.build({
roles: [role],
school,
});

return { school, user };
};

describe('isApplicable', () => {
describe('when object is instance of School', () => {
const setup = () => {
const { user, school } = setupSchoolAndUser();

return { user, school };
};

it('should return true', () => {
const { user, school } = setup();

const result = rule.isApplicable(user, school);

expect(result).toBe(true);
});
});

describe('when object is not instance of School', () => {
const setup = () => {
const { user } = setupSchoolAndUser();
const someRandomObject = { foo: 'bar' };

return { user, someRandomObject };
};

it('should return false', () => {
const { user, someRandomObject } = setup();

// @ts-expect-error Testcase
const result = rule.isApplicable(user, someRandomObject);

expect(result).toBe(false);
});
});
});

describe('hasPermission', () => {
describe('when user has required permissions and it is her school', () => {
const setup = () => {
const { user, school } = setupSchoolAndUser();
const context = AuthorizationContextBuilder.read([permissionA]);

return { user, school, context };
};

it('should return true', () => {
const { user, school, context } = setup();

const result = rule.hasPermission(user, school, context);

expect(result).toBe(true);
});
});

describe('when user does not have required permissions', () => {
const setup = () => {
const { user, school } = setupSchoolAndUser();
const context = AuthorizationContextBuilder.read([permissionB]);

return { user, school, context };
};

it('should return false', () => {
const { user, school, context } = setup();

const result = rule.hasPermission(user, school, context);

expect(result).toBe(false);
});
});

describe('when it is not the users school', () => {
const setup = () => {
const { user } = setupSchoolAndUser();
const someOtherSchool = schoolFactory.build();
const context = AuthorizationContextBuilder.read([]);

return { user, someOtherSchool, context };
};

it('should return false', () => {
const { user, someOtherSchool, context } = setup();

const result = rule.hasPermission(user, someOtherSchool, context);

expect(result).toBe(false);
});
});
});
});
27 changes: 27 additions & 0 deletions apps/server/src/modules/authorization/domain/rules/school.rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from '@nestjs/common';
import { AuthorizableObject } from '@shared/domain/domain-object';
import { User } from '@shared/domain/entity';
import { School } from '@src/modules/school/domain/do';
import { AuthorizationHelper } from '../service/authorization.helper';
import { AuthorizationContext, Rule } from '../type';

@Injectable()
export class SchoolRule implements Rule {
constructor(private readonly authorizationHelper: AuthorizationHelper) {}

public isApplicable(user: User, object: AuthorizableObject): boolean {
const isApplicable = object instanceof School;

return isApplicable;
}

public hasPermission(user: User, school: School, context: AuthorizationContext): boolean {
const hasRequiredPermissions = this.authorizationHelper.hasAllPermissions(user, context.requiredPermissions);

const isUsersSchool = user.school.id === school.id;

const hasPermission = hasRequiredPermissions && isUsersSchool;

return hasPermission;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
LegacySchoolRule,
LessonRule,
SchoolExternalToolRule,
SchoolRule,
SchoolSystemOptionsRule,
SubmissionRule,
SystemRule,
Expand All @@ -36,6 +37,7 @@ describe('RuleManager', () => {
let boardDoRule: DeepMocked<BoardDoRule>;
let contextExternalToolRule: DeepMocked<ContextExternalToolRule>;
let userLoginMigrationRule: DeepMocked<UserLoginMigrationRule>;
let schoolRule: DeepMocked<SchoolRule>;
let groupRule: DeepMocked<GroupRule>;
let systemRule: DeepMocked<SystemRule>;
let schoolSystemOptionsRule: DeepMocked<SchoolSystemOptionsRule>;
Expand All @@ -59,6 +61,7 @@ describe('RuleManager', () => {
{ provide: BoardDoRule, useValue: createMock<BoardDoRule>() },
{ provide: ContextExternalToolRule, useValue: createMock<ContextExternalToolRule>() },
{ provide: UserLoginMigrationRule, useValue: createMock<UserLoginMigrationRule>() },
{ provide: SchoolRule, useValue: createMock<SchoolRule>() },
{ provide: SystemRule, useValue: createMock<SystemRule>() },
{ provide: SchoolSystemOptionsRule, useValue: createMock<SchoolSystemOptionsRule>() },
],
Expand All @@ -77,6 +80,7 @@ describe('RuleManager', () => {
boardDoRule = await module.get(BoardDoRule);
contextExternalToolRule = await module.get(ContextExternalToolRule);
userLoginMigrationRule = await module.get(UserLoginMigrationRule);
schoolRule = await module.get(SchoolRule);
groupRule = await module.get(GroupRule);
systemRule = await module.get(SystemRule);
schoolSystemOptionsRule = await module.get(SchoolSystemOptionsRule);
Expand Down Expand Up @@ -110,6 +114,7 @@ describe('RuleManager', () => {
boardDoRule.isApplicable.mockReturnValueOnce(false);
contextExternalToolRule.isApplicable.mockReturnValueOnce(false);
userLoginMigrationRule.isApplicable.mockReturnValueOnce(false);
schoolRule.isApplicable.mockReturnValueOnce(false);
groupRule.isApplicable.mockReturnValueOnce(false);
systemRule.isApplicable.mockReturnValueOnce(false);
schoolSystemOptionsRule.isApplicable.mockReturnValueOnce(false);
Expand All @@ -134,6 +139,7 @@ describe('RuleManager', () => {
expect(boardDoRule.isApplicable).toBeCalled();
expect(contextExternalToolRule.isApplicable).toBeCalled();
expect(userLoginMigrationRule.isApplicable).toBeCalled();
expect(schoolRule.isApplicable).toBeCalled();
expect(groupRule.isApplicable).toBeCalled();
expect(systemRule.isApplicable).toBeCalled();
expect(schoolSystemOptionsRule.isApplicable).toBeCalled();
Expand Down Expand Up @@ -166,6 +172,7 @@ describe('RuleManager', () => {
boardDoRule.isApplicable.mockReturnValueOnce(false);
contextExternalToolRule.isApplicable.mockReturnValueOnce(false);
userLoginMigrationRule.isApplicable.mockReturnValueOnce(false);
schoolRule.isApplicable.mockReturnValueOnce(false);
groupRule.isApplicable.mockReturnValueOnce(false);
systemRule.isApplicable.mockReturnValueOnce(false);
schoolSystemOptionsRule.isApplicable.mockReturnValueOnce(false);
Expand Down Expand Up @@ -198,6 +205,7 @@ describe('RuleManager', () => {
boardDoRule.isApplicable.mockReturnValueOnce(false);
contextExternalToolRule.isApplicable.mockReturnValueOnce(false);
userLoginMigrationRule.isApplicable.mockReturnValueOnce(false);
schoolRule.isApplicable.mockReturnValueOnce(false);
groupRule.isApplicable.mockReturnValueOnce(false);
systemRule.isApplicable.mockReturnValueOnce(false);
schoolSystemOptionsRule.isApplicable.mockReturnValueOnce(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
LegacySchoolRule,
LessonRule,
SchoolExternalToolRule,
SchoolRule,
SchoolSystemOptionsRule,
SubmissionRule,
SystemRule,
Expand Down Expand Up @@ -38,6 +39,7 @@ export class RuleManager {
private readonly boardDoRule: BoardDoRule,
private readonly contextExternalToolRule: ContextExternalToolRule,
private readonly userLoginMigrationRule: UserLoginMigrationRule,
private readonly schoolRule: SchoolRule,
private readonly groupRule: GroupRule,
private readonly systemRule: SystemRule,
private readonly schoolSystemOptionsRule: SchoolSystemOptionsRule
Expand All @@ -55,6 +57,7 @@ export class RuleManager {
this.boardDoRule,
this.contextExternalToolRule,
this.userLoginMigrationRule,
this.schoolRule,
this.groupRule,
this.systemRule,
this.schoolSystemOptionsRule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,12 @@ describe('course copy service', () => {
const status = await service.copyCourse({ userId: user.id, courseId: course.id });
const courseCopy = status.copyEntity as Course;

expect(courseCopy.startDate).toEqual(user.school.schoolYear?.startDate);
expect(courseCopy.startDate).toEqual(user.school.currentYear?.startDate);
});

it('should set start and end-date of course to undefined when school year is undefined', async () => {
const { course, user } = setup();
user.school.schoolYear = undefined;
user.school.currentYear = undefined;
const status = await service.copyCourse({ userId: user.id, courseId: course.id });
const courseCopy = status.copyEntity as Course;

Expand All @@ -300,7 +300,7 @@ describe('course copy service', () => {
const status = await service.copyCourse({ userId: user.id, courseId: course.id });
const courseCopy = status.copyEntity as Course;

expect(courseCopy.untilDate).toEqual(user.school.schoolYear?.endDate);
expect(courseCopy.untilDate).toEqual(user.school.currentYear?.endDate);
});

it('should set color of course', async () => {
Expand Down Expand Up @@ -363,12 +363,12 @@ describe('course copy service', () => {
const status = await service.copyCourse({ userId: user.id, courseId: course.id });
const courseCopy = status.copyEntity as Course;

expect(courseCopy.startDate).toEqual(user.school.schoolYear?.startDate);
expect(courseCopy.startDate).toEqual(user.school.currentYear?.startDate);
});

it('should set start date and until date of course to undefined when school year is undefined', async () => {
const { course, user } = setup();
user.school.schoolYear = undefined;
user.school.currentYear = undefined;
const status = await service.copyCourse({ userId: user.id, courseId: course.id });
const courseCopy = status.copyEntity as Course;

Expand All @@ -381,7 +381,7 @@ describe('course copy service', () => {
const status = await service.copyCourse({ userId: user.id, courseId: course.id });
const courseCopy = status.copyEntity as Course;

expect(courseCopy.untilDate).toEqual(user.school.schoolYear?.endDate);
expect(courseCopy.untilDate).toEqual(user.school.currentYear?.endDate);
});

it('should set color of course', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export class CourseCopyService {
name: copyName,
color: originalCourse.color,
teachers: [user],
startDate: user.school.schoolYear?.startDate,
untilDate: user.school.schoolYear?.endDate,
startDate: user.school.currentYear?.startDate,
untilDate: user.school.currentYear?.endDate,
copyingSince: new Date(),
});

Expand Down
Loading

0 comments on commit ef05399

Please sign in to comment.