Skip to content

Commit

Permalink
BC-8187 - Creating and using boards in rooms (#5313)
Browse files Browse the repository at this point in the history
* implement board creation in room

* add api endpoint for getting room boards

* add room context to board context service

* add tests

---------

Co-authored-by: MartinSchuhmacher <[email protected]>
  • Loading branch information
uidp and MartinSchuhmacher authored Nov 8, 2024
1 parent 14600ef commit ac0f0dc
Show file tree
Hide file tree
Showing 32 changed files with 1,665 additions and 139 deletions.
91 changes: 0 additions & 91 deletions TODO.md

This file was deleted.

3 changes: 2 additions & 1 deletion apps/server/src/modules/board/board-api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import {
import { BoardModule } from './board.module';
import { BoardNodePermissionService } from './service';
import { BoardUc, CardUc, ColumnUc, ElementUc, SubmissionItemUc } from './uc';
import { RoomMemberModule } from '../room-member';

@Module({
imports: [BoardModule, LoggerModule, forwardRef(() => AuthorizationModule)],
imports: [BoardModule, LoggerModule, RoomMemberModule, forwardRef(() => AuthorizationModule)],
controllers: [BoardController, ColumnController, CardController, ElementController, BoardSubmissionController],
providers: [BoardUc, BoardNodePermissionService, ColumnUc, CardUc, ElementUc, SubmissionItemUc, CourseRepo],
})
Expand Down
9 changes: 5 additions & 4 deletions apps/server/src/modules/board/board-ws-api.module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { AuthorizationModule } from '@modules/authorization';
import { UserModule } from '@modules/user';
import { forwardRef, Module } from '@nestjs/common';
import { CourseRepo } from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import { AuthorizationModule } from '@modules/authorization';
import { UserModule } from '@modules/user';
import { RoomMemberModule } from '../room-member';
import { BoardModule } from './board.module';
import { BoardCollaborationGateway } from './gateway/board-collaboration.gateway';
import { MetricsService } from './metrics/metrics.service';
import { BoardUc, CardUc, ColumnUc, ElementUc } from './uc';
import { BoardNodePermissionService } from './service';
import { BoardUc, CardUc, ColumnUc, ElementUc } from './uc';

@Module({
imports: [BoardModule, forwardRef(() => AuthorizationModule), LoggerModule, UserModule],
imports: [BoardModule, forwardRef(() => AuthorizationModule), LoggerModule, UserModule, RoomMemberModule],
providers: [
BoardCollaborationGateway,
BoardNodePermissionService,
Expand Down
6 changes: 4 additions & 2 deletions apps/server/src/modules/board/board.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { CourseRepo } from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import { AuthorizationModule } from '../authorization';
import { RoomMemberModule } from '../room-member';
import { BoardNodeRule } from './authorisation/board-node.rule';
import { BoardNodeFactory } from './domain';
import { BoardNodeRepo } from './repo';
import {
Expand All @@ -30,8 +33,6 @@ import {
ColumnBoardTitleService,
ContentElementUpdateService,
} from './service/internal';
import { BoardNodeRule } from './authorisation/board-node.rule';
import { AuthorizationModule } from '../authorization';

@Module({
imports: [
Expand All @@ -45,6 +46,7 @@ import { AuthorizationModule } from '../authorization';
CqrsModule,
CollaborativeTextEditorModule,
AuthorizationModule,
RoomMemberModule,
],
providers: [
// TODO: move BoardDoAuthorizableService, BoardDoRepo, BoardDoService, BoardNodeRepo in separate module and move mediaboard related services in mediaboard module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { BoardExternalReferenceType } from '../../domain';

const baseRouteName = '/boards';

describe('board get context (api)', () => {
describe('board get context in course (api)', () => {
let app: INestApplication;
let em: EntityManager;
let testApiClient: TestApiClient;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { EntityManager } from '@mikro-orm/mongodb';
import { ServerTestModule } from '@modules/server';
import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { TestApiClient, cleanupCollections, groupEntityFactory, roleFactory, userFactory } from '@shared/testing';
import { Permission, RoleName } from '@shared/domain/interface';
import { accountFactory } from '@src/modules/account/testing';
import { GroupEntityTypes } from '@src/modules/group/entity';
import { roomMemberEntityFactory } from '@src/modules/room-member/testing';
import { roomEntityFactory } from '@src/modules/room/testing';
import { columnBoardEntityFactory } from '../../testing';
import { BoardExternalReferenceType } from '../../domain';

const baseRouteName = '/boards';

describe('board get context in room (api)', () => {
let app: INestApplication;
let em: EntityManager;
let testApiClient: TestApiClient;

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [ServerTestModule],
}).compile();

app = module.createNestApplication();
await app.init();
em = module.get(EntityManager);
testApiClient = new TestApiClient(app, baseRouteName);
});

afterAll(async () => {
await app.close();
});

beforeEach(async () => {
await cleanupCollections(em);
});

const setup = async () => {
const userWithEditRole = userFactory.buildWithId();
const accountWithEditRole = accountFactory.withUser(userWithEditRole).build();

const userWithViewRole = userFactory.buildWithId();
const accountWithViewRole = accountFactory.withUser(userWithViewRole).build();

const noAccessUser = userFactory.buildWithId();
const noAccessAccount = accountFactory.withUser(noAccessUser).build();

const roleRoomEdit = roleFactory.buildWithId({
name: RoleName.ROOM_EDITOR,
permissions: [Permission.ROOM_EDIT],
});
const roleRoomView = roleFactory.buildWithId({
name: RoleName.ROOM_VIEWER,
permissions: [Permission.ROOM_VIEW],
});

const userGroup = groupEntityFactory.buildWithId({
type: GroupEntityTypes.ROOM,
users: [
{ user: userWithEditRole, role: roleRoomEdit },
{ user: userWithViewRole, role: roleRoomView },
],
});

const room = roomEntityFactory.buildWithId();

const roomMember = roomMemberEntityFactory.build({ roomId: room.id, userGroupId: userGroup.id });

await em.persistAndFlush([
accountWithEditRole,
accountWithViewRole,
noAccessAccount,
userWithEditRole,
userWithViewRole,
noAccessUser,
roleRoomEdit,
roleRoomView,
userGroup,
room,
roomMember,
]);

const columnBoardNode = columnBoardEntityFactory.build({
isVisible: false,
context: { id: room.id, type: BoardExternalReferenceType.Room },
});

await em.persistAndFlush([columnBoardNode]);
em.clear();

return { accountWithEditRole, accountWithViewRole, noAccessAccount, columnBoardNode };
};

describe('with user who has edit role in room', () => {
it('should return status 200', async () => {
const { accountWithEditRole, columnBoardNode } = await setup();

const loggedInClient = await testApiClient.login(accountWithEditRole);

const response = await loggedInClient.get(`${columnBoardNode.id}/context`);

expect(response.status).toEqual(200);
});

it('should return the context', async () => {
const { accountWithEditRole, columnBoardNode } = await setup();

const loggedInClient = await testApiClient.login(accountWithEditRole);

const response = await loggedInClient.get(`${columnBoardNode.id}/context`);

expect(response.body).toEqual({ id: columnBoardNode.context?.id, type: columnBoardNode.context?.type });
});
});

describe('with user who has only view role in room', () => {
it('should return status 403', async () => {
const { accountWithViewRole, columnBoardNode } = await setup();

const loggedInClient = await testApiClient.login(accountWithViewRole);

const response = await loggedInClient.get(`${columnBoardNode.id}/context`);

expect(response.status).toEqual(403);
});
});

describe('with user who is not part of the room', () => {
it('should return status 403', async () => {
const { noAccessAccount, columnBoardNode } = await setup();

const loggedInClient = await testApiClient.login(noAccessAccount);

const response = await loggedInClient.get(`${columnBoardNode.id}/context`);

expect(response.status).toEqual(403);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { BoardExternalReferenceType } from '../../domain';

const baseRouteName = '/boards';

describe(`board copy (api)`, () => {
describe(`board copy with course relation (api)`, () => {
let app: INestApplication;
let em: EntityManager;
let testApiClient: TestApiClient;
Expand Down Expand Up @@ -39,13 +39,13 @@ describe(`board copy (api)`, () => {
const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher();

const course = courseFactory.build({ teachers: [teacherUser] });
await em.persistAndFlush([teacherUser, course]);
await em.persistAndFlush([teacherAccount, teacherUser, course]);

const columnBoardNode = columnBoardEntityFactory.build({
context: { id: course.id, type: BoardExternalReferenceType.Course },
});

await em.persistAndFlush([teacherAccount, teacherUser, columnBoardNode]);
await em.persistAndFlush([columnBoardNode]);
em.clear();

const loggedInClient = await testApiClient.login(teacherAccount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { CreateBoardBodyParams } from '../dto';

const baseRouteName = '/boards';

describe(`create board (api)`, () => {
describe(`create board in course (api)`, () => {
let app: INestApplication;
let em: EntityManager;
let testApiClient: TestApiClient;
Expand Down
Loading

0 comments on commit ac0f0dc

Please sign in to comment.