Skip to content

Commit

Permalink
N21-1766 Add course sync to provisioning (#4807)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinOehlerkingCap authored Mar 11, 2024
1 parent 127460a commit 6036879
Show file tree
Hide file tree
Showing 66 changed files with 2,279 additions and 1,291 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Configuration } from '@hpi-schul-cloud/commons/lib';
import { PseudonymModule } from '@modules/pseudonym';
import { ToolModule } from '@modules/tool';
import { UserModule } from '@modules/user';
import { UserModule } from '@modules/user/user.module';
import { HttpModule } from '@nestjs/axios';
import { Module, Provider } from '@nestjs/common';
import { LtiToolRepo } from '@shared/repo/ltitool/';
Expand Down
3 changes: 1 addition & 2 deletions apps/server/src/modules/account/uc/account.uc.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AccountService } from '@modules/account/services/account.service';
import { AccountDto } from '@modules/account/services/dto/account.dto';
import { ICurrentUser } from '@modules/authentication';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
Expand All @@ -13,8 +14,6 @@ import { Permission, RoleName } from '@shared/domain/interface';
import { PermissionService } from '@shared/domain/service';
import { EntityId } from '@shared/domain/types';
import { UserRepo } from '@shared/repo';

import { ICurrentUser } from '@modules/authentication';
import { BruteForcePrevention } from '@src/imports-from-feathers';
import { ObjectId } from 'bson';
import { AccountConfig } from '../account-config';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BoardModule } from '@modules/board';
import { ToolModule } from '@modules/tool';
import { BoardModule } from '@modules/board/board.module';
import { LessonModule } from '@modules/lesson/lesson.module';
import { ToolModule } from '@modules/tool/tool.module';
import { forwardRef, Module } from '@nestjs/common';
import {
CourseGroupRepo,
Expand All @@ -12,7 +13,6 @@ import {
UserRepo,
} from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import { LessonModule } from '../lesson';
import { AuthorizationModule } from './authorization.module';
import { AuthorizationHelper, AuthorizationReferenceService, ReferenceLoader } from './domain';

Expand All @@ -22,14 +22,7 @@ import { AuthorizationHelper, AuthorizationReferenceService, ReferenceLoader } f
* Avoid using this module and load the needed data in your use cases and then use the normal AuthorizationModule!
*/
@Module({
// TODO: remove forwardRef to TooModule N21-1055
imports: [
AuthorizationModule,
LessonModule,
forwardRef(() => ToolModule),
forwardRef(() => BoardModule),
LoggerModule,
],
imports: [AuthorizationModule, LessonModule, ToolModule, forwardRef(() => BoardModule), LoggerModule],
providers: [
AuthorizationHelper,
ReferenceLoader,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DeepPartial } from '@mikro-orm/core';
import { NotImplementedException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { LessonEntity, User } from '@shared/domain/entity';
import { Permission, RoleName } from '@shared/domain/interface';
Expand All @@ -10,13 +11,12 @@ import {
setupEntities,
userFactory,
} from '@shared/testing';
import { NotImplementedException } from '@nestjs/common';
import { Action, AuthorizationContext } from '../type';
import { AuthorizationContextBuilder } from '../mapper';
import { AuthorizationHelper } from '../service/authorization.helper';
import { Action, AuthorizationContext } from '../type';
import { CourseGroupRule } from './course-group.rule';
import { CourseRule } from './course.rule';
import { LessonRule } from './lesson.rule';
import { AuthorizationContextBuilder } from '../mapper';

describe('LessonRule', () => {
let rule: LessonRule;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { DeepPartial } from '@mikro-orm/core';
import { Test, TestingModule } from '@nestjs/testing';
import { Permission, RoleName } from '@shared/domain/interface';
import { courseFactory, lessonFactory, roleFactory, setupEntities, taskFactory, userFactory } from '@shared/testing';
import { Action } from '../type';
import { AuthorizationHelper } from '../service/authorization.helper';
import { Action } from '../type';
import { CourseGroupRule } from './course-group.rule';
import { TaskRule } from './task.rule';
import { CourseRule } from './course.rule';
import { LessonRule } from './lesson.rule';
import { TaskRule } from './task.rule';

describe('TaskRule', () => {
let service: TaskRule;
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/modules/board/board.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FilesStorageClientModule } from '@modules/files-storage-client';
import { TldrawClientModule } from '@modules/tldraw-client';
import { ContextExternalToolModule } from '@modules/tool/context-external-tool';
import { ToolConfigModule } from '@modules/tool/tool-config.module';
import { UserModule } from '@modules/user';
import { UserModule } from '@modules/user/user.module';
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { ContentElementFactory } from '@shared/domain/domainobject';
Expand Down
1 change: 0 additions & 1 deletion apps/server/src/modules/board/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { BoardModule } from './board.module';
export * from './service/board-do-authorizable.service';
export * from './service/card.service';
export * from './service/column-board.service';
Expand Down
28 changes: 14 additions & 14 deletions apps/server/src/modules/deletion/deletion-api.module.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { Module } from '@nestjs/common';
import { DeletionModule } from '@modules/deletion';
import { Configuration } from '@hpi-schul-cloud/commons';
import { AccountModule } from '@modules/account';
import { AuthenticationModule } from '@modules/authentication';
import { ClassModule } from '@modules/class';
import { LearnroomModule } from '@modules/learnroom';
import { DeletionModule } from '@modules/deletion';
import { FilesModule } from '@modules/files';
import { PseudonymModule } from '@modules/pseudonym';
import { FilesStorageClientModule } from '@modules/files-storage-client';
import { LearnroomModule } from '@modules/learnroom';
import { LessonModule } from '@modules/lesson';
import { TeamsModule } from '@modules/teams';
import { UserModule } from '@modules/user';
import { LoggerModule } from '@src/core/logger';
import { AuthenticationModule } from '@modules/authentication';
import { RocketChatUserModule } from '@modules/rocketchat-user';
import { Configuration } from '@hpi-schul-cloud/commons';
import { RocketChatModule } from '@modules/rocketchat';
import { PseudonymModule } from '@modules/pseudonym';
import { RegistrationPinModule } from '@modules/registration-pin';
import { RocketChatModule } from '@modules/rocketchat';
import { RocketChatUserModule } from '@modules/rocketchat-user';
import { TaskModule } from '@modules/task';
import { FilesStorageClientModule } from '@modules/files-storage-client';
import { DeletionRequestsController } from './controller/deletion-requests.controller';
import { TeamsModule } from '@modules/teams';
import { UserModule } from '@modules/user/user.module';
import { Module } from '@nestjs/common';
import { LoggerModule } from '@src/core/logger';
import { NewsModule } from '../news';
import { DeletionExecutionsController } from './controller/deletion-executions.controller';
import { DeletionRequestsController } from './controller/deletion-requests.controller';
import { DeletionRequestUc } from './uc';
import { NewsModule } from '../news';

@Module({
imports: [
Expand Down
10 changes: 9 additions & 1 deletion apps/server/src/modules/group/domain/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ export class Group extends DomainObject<GroupProps> {
return this.props.type;
}

get validFrom(): Date | undefined {
return this.props.validFrom;
}

get validUntil(): Date | undefined {
return this.props.validUntil;
}

removeUser(user: UserDO): void {
this.props.users = this.props.users.filter((groupUser: GroupUser): boolean => groupUser.userId !== user.id);
}
Expand All @@ -56,7 +64,7 @@ export class Group extends DomainObject<GroupProps> {
}

addUser(user: GroupUser): void {
if (!this.users.find((u) => u.userId === user.userId)) {
if (!this.users.find((u: GroupUser): boolean => u.userId === user.userId)) {
this.users.push(user);
}
}
Expand Down
8 changes: 4 additions & 4 deletions apps/server/src/modules/group/group-api.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Module } from '@nestjs/common';
import { AuthorizationModule } from '@modules/authorization';
import { ClassModule } from '@modules/class';
import { RoleModule } from '@modules/role';
import { LegacySchoolModule } from '@modules/legacy-school';
import { RoleModule } from '@modules/role';
import { SchoolModule } from '@modules/school';
import { SystemModule } from '@modules/system';
import { UserModule } from '@modules/user';
import { UserModule } from '@modules/user/user.module';
import { Module } from '@nestjs/common';
import { LoggerModule } from '@src/core/logger';
import { SchoolModule } from '@modules/school';
import { GroupController } from './controller';
import { GroupModule } from './group.module';
import { GroupUc } from './uc';
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/modules/group/group.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { LearnroomModule } from '@modules/learnroom/learnroom.module';
import { Module } from '@nestjs/common';
import { GroupRepo } from './repo';
import { GroupService } from './service';

@Module({
imports: [LearnroomModule],
providers: [GroupRepo, GroupService],
exports: [GroupService],
})
Expand Down
33 changes: 32 additions & 1 deletion apps/server/src/modules/group/service/group.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { ObjectId } from '@mikro-orm/mongodb';
import { Course } from '@modules/learnroom/domain';
import { CourseService } from '@modules/learnroom/service/course.service';
import { courseFactory } from '@modules/learnroom/testing';
import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundLoggableException } from '@shared/common/loggable-exception';
import { UserDO } from '@shared/domain/domainobject';
Expand All @@ -13,6 +16,7 @@ describe('GroupService', () => {
let service: GroupService;

let groupRepo: DeepMocked<GroupRepo>;
let courseService: DeepMocked<CourseService>;

beforeAll(async () => {
module = await Test.createTestingModule({
Expand All @@ -22,11 +26,16 @@ describe('GroupService', () => {
provide: GroupRepo,
useValue: createMock<GroupRepo>(),
},
{
provide: CourseService,
useValue: createMock<CourseService>(),
},
],
}).compile();

service = module.get(GroupService);
groupRepo = module.get(GroupRepo);
courseService = module.get(CourseService);
});

afterAll(async () => {
Expand Down Expand Up @@ -290,12 +299,20 @@ describe('GroupService', () => {
});

describe('delete', () => {
describe('when saving a group', () => {
describe('when deleting a group', () => {
const setup = () => {
const group: Group = groupFactory.build();
const course: Course = courseFactory.build({
syncedWithGroup: group.id,
teacherIds: [new ObjectId().toHexString()],
studentIds: [new ObjectId().toHexString()],
});

courseService.findBySyncedGroup.mockResolvedValueOnce([course]);

return {
group,
course,
};
};

Expand All @@ -306,6 +323,20 @@ describe('GroupService', () => {

expect(groupRepo.delete).toHaveBeenCalledWith(group);
});

it('should remove all sync references from courses', async () => {
const { group, course } = setup();

await service.delete(group);

expect(courseService.saveAll).toHaveBeenCalledWith<[Course[]]>([
new Course({
...course.getProps(),
syncedWithGroup: undefined,
studentIds: [],
}),
]);
});
});
});

Expand Down
17 changes: 16 additions & 1 deletion apps/server/src/modules/group/service/group.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { AuthorizationLoaderServiceGeneric } from '@modules/authorization';
import { Course } from '@modules/learnroom/domain';
import { CourseService } from '@modules/learnroom/service/course.service';
import { Injectable } from '@nestjs/common';
import { NotFoundLoggableException } from '@shared/common/loggable-exception';
import { type UserDO } from '@shared/domain/domainobject';
Expand All @@ -8,7 +10,7 @@ import { GroupRepo } from '../repo';

@Injectable()
export class GroupService implements AuthorizationLoaderServiceGeneric<Group> {
constructor(private readonly groupRepo: GroupRepo) {}
constructor(private readonly groupRepo: GroupRepo, private readonly courseService: CourseService) {}

public async findById(id: EntityId): Promise<Group> {
const group: Group | null = await this.groupRepo.findGroupById(id);
Expand Down Expand Up @@ -66,5 +68,18 @@ export class GroupService implements AuthorizationLoaderServiceGeneric<Group> {

public async delete(group: Group): Promise<void> {
await this.groupRepo.delete(group);

await this.removeCourseSyncReference(group);
}

private async removeCourseSyncReference(group: Group) {
const courses: Course[] = await this.courseService.findBySyncedGroup(group);

courses.forEach((course: Course): void => {
course.studentIds = [];
course.syncedWithGroup = undefined;
});

await this.courseService.saveAll(courses);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { S3ClientModule } from '@infra/s3-client';
import { AuthenticationModule } from '@modules/authentication';
import { AuthenticationApiModule } from '@modules/authentication/authentication-api.module';
import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module';
import { UserModule } from '@modules/user';
import { UserModule } from '@modules/user/user.module';
import { DynamicModule, Module } from '@nestjs/common';
import { ALL_ENTITIES } from '@shared/domain/entity';
import { CoreModule } from '@src/core';
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/modules/h5p-editor/h5p-editor.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { Dictionary, IPrimaryKey } from '@mikro-orm/core';
import { MikroOrmModule, MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs';
import { AuthenticationModule } from '@modules/authentication';
import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module';
import { UserModule } from '@modules/user';
import { UserModule } from '@modules/user/user.module';
import { Module, NotFoundException } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { ALL_ENTITIES } from '@shared/domain/entity';
import { DB_PASSWORD, DB_URL, DB_USERNAME, createConfigModuleOptions } from '@src/config';
import { createConfigModuleOptions, DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/config';
import { CoreModule } from '@src/core';
import { Logger } from '@src/core/logger';
import { H5PEditorController } from './controller/h5p-editor.controller';
Expand Down
32 changes: 30 additions & 2 deletions apps/server/src/modules/learnroom/domain/do/course.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { EntityId } from '@shared/domain/types';
export interface CourseProps extends AuthorizableObject {
name: string;

description: string;
description?: string;

schoolId: EntityId;

Expand Down Expand Up @@ -36,4 +36,32 @@ export interface CourseProps extends AuthorizableObject {
syncedWithGroup?: EntityId;
}

export class Course extends DomainObject<CourseProps> {}
export class Course extends DomainObject<CourseProps> {
get name(): string {
return this.props.name;
}

set name(value: string) {
this.props.name = value;
}

set studentIds(value: EntityId[]) {
this.props.studentIds = value;
}

set teacherIds(value: EntityId[]) {
this.props.teacherIds = value;
}

set startDate(value: Date | undefined) {
this.props.startDate = value;
}

set untilDate(value: Date | undefined) {
this.props.untilDate = value;
}

set syncedWithGroup(value: EntityId | undefined) {
this.props.syncedWithGroup = value;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Group } from '@modules/group';
import { EntityId } from '@shared/domain/types';
import { BaseDomainObjectRepoInterface } from '@shared/repo/base-domain-object.repo.interface';
import { Course } from '../do';

export interface CourseRepo extends BaseDomainObjectRepoInterface<Course> {
findCourseById(id: EntityId): Promise<Course>;

findBySyncedGroup(group: Group): Promise<Course[]>;
}

export const COURSE_REPO = Symbol('COURSE_REPO');
2 changes: 1 addition & 1 deletion apps/server/src/modules/learnroom/learnroom.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BoardModule } from '@modules/board';
import { BoardModule } from '@modules/board/board.module';
import { CopyHelperModule } from '@modules/copy-helper';
import { LessonModule } from '@modules/lesson';
import { TaskModule } from '@modules/task';
Expand Down
Loading

0 comments on commit 6036879

Please sign in to comment.