Skip to content

Commit

Permalink
N21-2287 Fix partial course sync start for admins (#5343)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinOehlerkingCap authored and hoeppner-dataport committed Nov 18, 2024
1 parent bf5fec9 commit 4ad1a0e
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 90 deletions.
118 changes: 115 additions & 3 deletions apps/server/src/modules/group/domain/group.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { RoleReference, UserDO } from '@shared/domain/domainobject';
import { groupFactory, roleFactory, userDoFactory } from '@shared/testing';

import { ObjectId } from '@mikro-orm/mongodb';
import { Group } from './group';
import { GroupUser } from './group-user';

describe('Group (Domain Object)', () => {
describe(Group.name, () => {
describe('removeUser', () => {
describe('when the user is in the group', () => {
const setup = () => {
Expand Down Expand Up @@ -193,4 +192,117 @@ describe('Group (Domain Object)', () => {
});
});
});

describe('isMember', () => {
describe('when the user is a member of the group', () => {
const setup = () => {
const userId = new ObjectId().toHexString();
const roleId = new ObjectId().toHexString();
const groupUser = new GroupUser({
userId,
roleId,
});
const group = groupFactory.build({
users: [groupUser],
});

return {
group,
userId,
};
};

it('should return true', () => {
const { group, userId } = setup();

const result = group.isMember(userId);

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

describe('when the user is a member of the group and has the requested role', () => {
const setup = () => {
const userId = new ObjectId().toHexString();
const roleId = new ObjectId().toHexString();
const groupUser = new GroupUser({
userId,
roleId,
});
const group = groupFactory.build({
users: [groupUser],
});

return {
group,
userId,
roleId,
};
};

it('should return true', () => {
const { group, userId, roleId } = setup();

const result = group.isMember(userId, roleId);

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

describe('when the user is a member of the group, but has a different role', () => {
const setup = () => {
const userId = new ObjectId().toHexString();
const roleId = new ObjectId().toHexString();
const groupUser = new GroupUser({
userId,
roleId: new ObjectId().toHexString(),
});
const group = groupFactory.build({
users: [groupUser],
});

return {
group,
userId,
roleId,
};
};

it('should return false', () => {
const { group, userId, roleId } = setup();

const result = group.isMember(userId, roleId);

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

describe('when the user is not a member of the group', () => {
const setup = () => {
const userId = new ObjectId().toHexString();
const roleId = new ObjectId().toHexString();
const groupUser = new GroupUser({
userId: new ObjectId().toHexString(),
roleId: new ObjectId().toHexString(),
});
const group = groupFactory.build({
users: [groupUser],
});

return {
group,
userId,
roleId,
};
};

it('should return false', () => {
const { group, userId } = setup();

const result = group.isMember(userId);

expect(result).toEqual(false);
});
});
});
});
14 changes: 11 additions & 3 deletions apps/server/src/modules/group/domain/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,25 @@ export class Group extends DomainObject<GroupProps> {
return this.props.validPeriod;
}

removeUser(user: UserDO): void {
public removeUser(user: UserDO): void {
this.props.users = this.props.users.filter((groupUser: GroupUser): boolean => groupUser.userId !== user.id);
}

isEmpty(): boolean {
public isEmpty(): boolean {
return this.props.users.length === 0;
}

addUser(user: GroupUser): void {
public addUser(user: GroupUser): void {
if (!this.users.find((u: GroupUser): boolean => u.userId === user.userId)) {
this.users.push(user);
}
}

public isMember(userId: EntityId, roleId?: EntityId): boolean {
const isMember: boolean = this.users.some(
(groupUser: GroupUser) => groupUser.userId === userId && (roleId ? groupUser.roleId === roleId : true)
);

return isMember;
}
}
23 changes: 0 additions & 23 deletions apps/server/src/modules/group/service/group.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { ObjectId } from '@mikro-orm/mongodb';
import type { ProvisioningConfig } from '@modules/provisioning';
import { BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { EventBus } from '@nestjs/cqrs';
import { Test, TestingModule } from '@nestjs/testing';
Expand Down Expand Up @@ -561,16 +560,6 @@ describe('GroupService', () => {

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

describe('when the role id is undefined', () => {
it('should throw', async () => {
roleService.findByName.mockResolvedValue(roleDtoFactory.build({ id: undefined }));

await expect(service.addUserToGroup('groupId', 'userId', RoleName.STUDENT)).rejects.toThrow(
BadRequestException
);
});
});
});
});

Expand Down Expand Up @@ -618,18 +607,6 @@ describe('GroupService', () => {
});
});

describe('when role has no id', () => {
it('should fail', async () => {
const roleDto = roleDtoFactory.buildWithId({ name: RoleName.STUDENT });
roleDto.id = undefined;
const { userDo } = setup([roleDto]);

const method = () => service.addUsersToGroup('groupId', [{ userId: userDo.id!, roleName: RoleName.STUDENT }]);

await expect(method).rejects.toThrow();
});
});

describe('when user does not exist', () => {
it('should fail', async () => {
const roleDto = roleDtoFactory.buildWithId({ name: RoleName.STUDENT });
Expand Down
8 changes: 2 additions & 6 deletions apps/server/src/modules/group/service/group.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
GroupAggregateScope,
GroupDeletedEvent,
GroupFilter,
GroupVisibilityPermission,
GroupUser,
GroupTypes,
GroupUser,
GroupVisibilityPermission,
} from '../domain';
import { GroupRepo } from '../repo';

Expand Down Expand Up @@ -107,9 +107,6 @@ export class GroupService implements AuthorizationLoaderServiceGeneric<Group> {

public async addUserToGroup(groupId: EntityId, userId: EntityId, roleName: RoleName): Promise<void> {
const role = await this.roleService.findByName(roleName);
if (!role.id) {
throw new BadRequestException('Role has no id.');
}
const group = await this.findById(groupId);
const user = await this.userService.findById(userId);
// user must have an id, because we are fetching it by id -> fix in service
Expand All @@ -133,7 +130,6 @@ export class GroupService implements AuthorizationLoaderServiceGeneric<Group> {
for (const { userId, roleName } of userIdsAndRoles) {
const role = roleNamesSet.find((r) => r.name === roleName);
if (!role) throw new BadRequestException(`Role ${roleName} not found.`);
if (!role.id) throw new BadRequestException('Role has no id.');
const user = users.find((u) => u.id === userId);
// user must have an id, because we are fetching it by id -> fix in service
if (!user) throw new BadRequestException('Unknown userId.');
Expand Down
53 changes: 53 additions & 0 deletions apps/server/src/modules/learnroom/domain/do/course.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { courseFactory } from '../../testing';
import { Course } from './course';

describe(Course.name, () => {
describe('isTeacher', () => {
describe('when the user is a teacher in the course', () => {
const setup = () => {
const userId = new ObjectId().toHexString();
const course = courseFactory.build({
teacherIds: [userId],
});

return {
course,
userId,
};
};

it('should return true', () => {
const { course, userId } = setup();

const result = course.isTeacher(userId);

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

describe('when the user is not a teacher in the course', () => {
const setup = () => {
const userId = new ObjectId().toHexString();
const course = courseFactory.build({
teacherIds: [new ObjectId().toHexString()],
studentIds: [userId],
substitutionTeacherIds: [userId],
});

return {
course,
userId,
};
};

it('should return false', () => {
const { course, userId } = setup();

const result = course.isTeacher(userId);

expect(result).toEqual(false);
});
});
});
});
6 changes: 6 additions & 0 deletions apps/server/src/modules/learnroom/domain/do/course.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,10 @@ export class Course extends DomainObject<CourseProps> {
get excludeFromSync(): SyncAttribute[] | undefined {
return this.props.excludeFromSync;
}

public isTeacher(userId: EntityId): boolean {
const isTeacher: boolean = this.teachers.some((teacherId: EntityId) => teacherId === userId);

return isTeacher;
}
}
Loading

0 comments on commit 4ad1a0e

Please sign in to comment.