Skip to content

Commit

Permalink
BC-8193 - Expanding and registering BBB (#5354)
Browse files Browse the repository at this point in the history
* implementing board videoconf in VC service with tests

* recent state for implementing board authorization in VC

* implementing VC element and room + VC element scope

* adjusting VC scope to board element

* adding VC response and node

* add school id  indexes

* adding yellow to room color selector

* seting type alias for better maintainability

* implementing FEATURE_COLUMN_BOARD_VIDEOCONFERENCE_ENABLED for FE usage

---------

Co-authored-by: Uwe Ilgenstein <[email protected]>
  • Loading branch information
MartinSchuhmacher and uidp authored Dec 16, 2024
1 parent c8be3a7 commit 0b4dacc
Show file tree
Hide file tree
Showing 76 changed files with 3,661 additions and 82 deletions.
5 changes: 4 additions & 1 deletion apps/server/src/modules/board/controller/card.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
RenameBodyParams,
RichTextElementResponse,
SubmissionContainerElementResponse,
VideoConferenceElementResponse,
} from './dto';
import { SetHeightBodyParams } from './dto/board/set-height.body.params';
import { CardResponseMapper, ContentElementResponseFactory } from './mapper';
Expand Down Expand Up @@ -124,7 +125,8 @@ export class CardController {
RichTextElementResponse,
SubmissionContainerElementResponse,
DrawingElementResponse,
DeletedElementResponse
DeletedElementResponse,
VideoConferenceElementResponse
)
@ApiResponse({
status: 201,
Expand All @@ -137,6 +139,7 @@ export class CardController {
{ $ref: getSchemaPath(SubmissionContainerElementResponse) },
{ $ref: getSchemaPath(DrawingElementResponse) },
{ $ref: getSchemaPath(DeletedElementResponse) },
{ $ref: getSchemaPath(VideoConferenceElementResponse) },
],
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
LinkElementResponse,
RichTextElementResponse,
SubmissionContainerElementResponse,
VideoConferenceElementResponse,
} from '../element';
import { TimestampsResponse } from '../timestamps.response';
import { VisibilitySettingsResponse } from './visibility-settings.response';
Expand All @@ -22,7 +23,8 @@ import { VisibilitySettingsResponse } from './visibility-settings.response';
DrawingElementResponse,
SubmissionContainerElementResponse,
CollaborativeTextEditorElementResponse,
DeletedElementResponse
DeletedElementResponse,
VideoConferenceElementResponse
)
export class CardResponse {
constructor({ id, title, height, elements, visibilitySettings, timestamps }: CardResponse) {
Expand Down Expand Up @@ -58,6 +60,7 @@ export class CardResponse {
{ $ref: getSchemaPath(DrawingElementResponse) },
{ $ref: getSchemaPath(CollaborativeTextEditorElementResponse) },
{ $ref: getSchemaPath(DeletedElementResponse) },
{ $ref: getSchemaPath(VideoConferenceElementResponse) },
],
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FileElementResponse } from './file-element.response';
import { LinkElementResponse } from './link-element.response';
import { RichTextElementResponse } from './rich-text-element.response';
import { SubmissionContainerElementResponse } from './submission-container-element.response';
import { VideoConferenceElementResponse } from './video-conference-element.response';

export type AnyContentElementResponse =
| FileElementResponse
Expand All @@ -15,7 +16,8 @@ export type AnyContentElementResponse =
| ExternalToolElementResponse
| DrawingElementResponse
| CollaborativeTextEditorElementResponse
| DeletedElementResponse;
| DeletedElementResponse
| VideoConferenceElementResponse;

export const isFileElementResponse = (element: AnyContentElementResponse): element is FileElementResponse =>
element instanceof FileElementResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export * from './link-element.response';
export * from './rich-text-element.response';
export * from './submission-container-element.response';
export * from './update-element-content.body.params';
export * from './video-conference-element.response';
export * from './deleted-element.response';
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,29 @@ export class ExternalToolElementContentBody extends ElementContentBody {
content!: ExternalToolContentBody;
}

export class VideoConferenceContentBody {
@IsString()
@ApiProperty()
title!: string;
}

export class VideoConferenceElementContentBody extends ElementContentBody {
@ApiProperty({ type: ContentElementType.VIDEO_CONFERENCE })
type!: ContentElementType.VIDEO_CONFERENCE;

@ValidateNested()
@ApiProperty()
content!: VideoConferenceContentBody;
}

export type AnyElementContentBody =
| FileContentBody
| DrawingContentBody
| LinkContentBody
| RichTextContentBody
| SubmissionContainerContentBody
| ExternalToolContentBody;
| ExternalToolContentBody
| VideoConferenceContentBody;

export class UpdateElementContentBodyParams {
@ValidateNested()
Expand All @@ -156,6 +172,7 @@ export class UpdateElementContentBodyParams {
{ value: SubmissionContainerElementContentBody, name: ContentElementType.SUBMISSION_CONTAINER },
{ value: ExternalToolElementContentBody, name: ContentElementType.EXTERNAL_TOOL },
{ value: DrawingElementContentBody, name: ContentElementType.DRAWING },
{ value: VideoConferenceElementContentBody, name: ContentElementType.VIDEO_CONFERENCE },
],
},
keepDiscriminatorProperty: true,
Expand All @@ -168,6 +185,7 @@ export class UpdateElementContentBodyParams {
{ $ref: getSchemaPath(SubmissionContainerElementContentBody) },
{ $ref: getSchemaPath(ExternalToolElementContentBody) },
{ $ref: getSchemaPath(DrawingElementContentBody) },
{ $ref: getSchemaPath(VideoConferenceElementContentBody) },
],
})
data!:
Expand All @@ -176,5 +194,6 @@ export class UpdateElementContentBodyParams {
| RichTextElementContentBody
| SubmissionContainerElementContentBody
| ExternalToolElementContentBody
| DrawingElementContentBody;
| DrawingElementContentBody
| VideoConferenceElementContentBody;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ApiProperty } from '@nestjs/swagger';
import { ContentElementType } from '../../../domain';
import { TimestampsResponse } from '../timestamps.response';

export class VideoConferenceElementContent {
constructor({ title }: VideoConferenceElementContent) {
this.title = title;
}

@ApiProperty()
title: string;
}

export class VideoConferenceElementResponse {
constructor({ id, content, timestamps, type }: VideoConferenceElementResponse) {
this.id = id;
this.timestamps = timestamps;
this.type = type;
this.content = content;
}

@ApiProperty({ pattern: '[a-f0-9]{24}' })
id: string;

@ApiProperty({ enum: ContentElementType, enumName: 'ContentElementType' })
type: ContentElementType.VIDEO_CONFERENCE;

@ApiProperty()
timestamps: TimestampsResponse;

@ApiProperty()
content: VideoConferenceElementContent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import {
SubmissionContainerElementResponse,
SubmissionItemResponse,
UpdateElementContentBodyParams,
VideoConferenceElementContentBody,
VideoConferenceElementResponse,
} from './dto';
import { ContentElementResponseFactory, SubmissionItemResponseMapper } from './mapper';

Expand Down Expand Up @@ -71,7 +73,8 @@ export class ElementController {
SubmissionContainerElementContentBody,
ExternalToolElementContentBody,
LinkElementContentBody,
DrawingElementContentBody
DrawingElementContentBody,
VideoConferenceElementContentBody
)
@ApiResponse({
status: 200,
Expand All @@ -83,6 +86,7 @@ export class ElementController {
{ $ref: getSchemaPath(RichTextElementResponse) },
{ $ref: getSchemaPath(SubmissionContainerElementResponse) },
{ $ref: getSchemaPath(DrawingElementResponse) },
{ $ref: getSchemaPath(VideoConferenceElementResponse) },
],
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class CollaborativeTextEditorElementResponseMapper implements BaseRespons
return result;
}

canMap(element: CollaborativeTextEditorElement): boolean {
canMap(element: unknown): boolean {
return element instanceof CollaborativeTextEditorElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
linkElementFactory,
richTextElementFactory,
submissionContainerElementFactory,
videoConferenceElementFactory,
} from '../../testing';
import {
DeletedElementResponse,
Expand All @@ -14,6 +15,7 @@ import {
LinkElementResponse,
RichTextElementResponse,
SubmissionContainerElementResponse,
VideoConferenceElementResponse,
} from '../dto';
import { ContentElementResponseFactory } from './content-element-response.factory';

Expand Down Expand Up @@ -65,6 +67,14 @@ describe(ContentElementResponseFactory.name, () => {
expect(result).toBeInstanceOf(DeletedElementResponse);
});

it('should return instance of VideoConferenceElementResponse', () => {
const videoConferenceElement = videoConferenceElementFactory.build();

const result = ContentElementResponseFactory.mapToResponse(videoConferenceElement);

expect(result).toBeInstanceOf(VideoConferenceElementResponse);
});

it('should throw NotImplementedException', () => {
// @ts-expect-error check unknown type
expect(() => ContentElementResponseFactory.mapToResponse('UNKNOWN')).toThrow(NotImplementedException);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { FileElementResponseMapper } from './file-element-response.mapper';
import { LinkElementResponseMapper } from './link-element-response.mapper';
import { RichTextElementResponseMapper } from './rich-text-element-response.mapper';
import { SubmissionContainerElementResponseMapper } from './submission-container-element-response.mapper';
import { VideoConferenceElementResponseMapper } from './video-conference-element-response.mapper';

export class ContentElementResponseFactory {
private static mappers: BaseResponseMapper[] = [
Expand All @@ -27,6 +28,7 @@ export class ContentElementResponseFactory {
ExternalToolElementResponseMapper.getInstance(),
CollaborativeTextEditorElementResponseMapper.getInstance(),
DeletedElementResponseMapper.getInstance(),
VideoConferenceElementResponseMapper.getInstance(),
];

static mapToResponse(element: AnyBoardNode): AnyContentElementResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class DrawingElementResponseMapper implements BaseResponseMapper {
return result;
}

canMap(element: DrawingElement): boolean {
canMap(element: unknown): boolean {
return element instanceof DrawingElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class ExternalToolElementResponseMapper implements BaseResponseMapper {
return result;
}

canMap(element: ExternalToolElement): boolean {
canMap(element: unknown): boolean {
return element instanceof ExternalToolElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class FileElementResponseMapper implements BaseResponseMapper {
return result;
}

canMap(element: FileElement): boolean {
canMap(element: unknown): boolean {
return element instanceof FileElement;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/board/controller/mapper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export * from './link-element-response.mapper';
export * from './rich-text-element-response.mapper';
export * from './submission-container-element-response.mapper';
export * from './submission-item-response.mapper';
export * from './video-conference-element-response.mapper';
export * from './deleted-element-response.mapper';
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class LinkElementResponseMapper implements BaseResponseMapper {
return result;
}

canMap(element: LinkElement): boolean {
canMap(element: unknown): boolean {
return element instanceof LinkElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class RichTextElementResponseMapper implements BaseResponseMapper {
return result;
}

canMap(element: RichTextElement): boolean {
canMap(element: unknown): boolean {
return element instanceof RichTextElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class SubmissionContainerElementResponseMapper implements BaseResponseMap
return result;
}

canMap(element: SubmissionContainerElement): boolean {
canMap(element: unknown): boolean {
return element instanceof SubmissionContainerElement;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ContentElementType, VideoConferenceElement } from '../../domain';
import { TimestampsResponse, VideoConferenceElementContent, VideoConferenceElementResponse } from '../dto';
import { BaseResponseMapper } from './base-mapper.interface';

export class VideoConferenceElementResponseMapper implements BaseResponseMapper {
private static instance: VideoConferenceElementResponseMapper;

public static getInstance(): VideoConferenceElementResponseMapper {
if (!VideoConferenceElementResponseMapper.instance) {
VideoConferenceElementResponseMapper.instance = new VideoConferenceElementResponseMapper();
}

return VideoConferenceElementResponseMapper.instance;
}

mapToResponse(element: VideoConferenceElement): VideoConferenceElementResponse {
const result = new VideoConferenceElementResponse({
id: element.id,
timestamps: new TimestampsResponse({ lastUpdatedAt: element.updatedAt, createdAt: element.createdAt }),
type: ContentElementType.VIDEO_CONFERENCE,
content: new VideoConferenceElementContent({ title: element.title }),
});

return result;
}

canMap(element: unknown): boolean {
return element instanceof VideoConferenceElement;
}
}
7 changes: 7 additions & 0 deletions apps/server/src/modules/board/domain/board-node.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SubmissionContainerElement } from './submission-container-element.do';
import { SubmissionItem } from './submission-item.do';
import { handleNonExhaustiveSwitch } from './type-mapping';
import { AnyContentElement, BoardExternalReference, BoardLayout, BoardNodeProps, ContentElementType } from './types';
import { VideoConferenceElement } from './video-conference-element.do';

@Injectable()
export class BoardNodeFactory {
Expand Down Expand Up @@ -86,6 +87,12 @@ export class BoardNodeFactory {
...this.getBaseProps(),
});
break;
case ContentElementType.VIDEO_CONFERENCE:
element = new VideoConferenceElement({
...this.getBaseProps(),
title: '',
});
break;
default:
handleNonExhaustiveSwitch(type);
}
Expand Down
1 change: 1 addition & 0 deletions apps/server/src/modules/board/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export * from './submission-item.do';
export * from './path-utils';
export * from './types';
export * from './type-mapping';
export * from './video-conference-element.do';
export * from './deleted-element.do';
2 changes: 2 additions & 0 deletions apps/server/src/modules/board/domain/type-mapping.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
richTextElementFactory,
submissionContainerElementFactory,
submissionItemFactory,
videoConferenceElementFactory,
} from '../testing';

describe('getBoardNodeType', () => {
Expand All @@ -37,6 +38,7 @@ describe('getBoardNodeType', () => {
BoardNodeType.SUBMISSION_CONTAINER_ELEMENT
);
expect(getBoardNodeType(submissionItemFactory.build())).toBe(BoardNodeType.SUBMISSION_ITEM);
expect(getBoardNodeType(videoConferenceElementFactory.build())).toBe(BoardNodeType.VIDEO_CONFERENCE_ELEMENT);
});

it('should throw error for unknown type', () => {
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/modules/board/domain/type-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { SubmissionContainerElement } from './submission-container-element.do';
import { SubmissionItem } from './submission-item.do';
import type { AnyBoardNode } from './types/any-board-node';
import { BoardNodeType } from './types/board-node-type.enum';
import { VideoConferenceElement } from './video-conference-element.do';

// register node types
const BoardNodeTypeToConstructor = {
Expand All @@ -31,6 +32,7 @@ const BoardNodeTypeToConstructor = {
[BoardNodeType.RICH_TEXT_ELEMENT]: RichTextElement,
[BoardNodeType.SUBMISSION_CONTAINER_ELEMENT]: SubmissionContainerElement,
[BoardNodeType.SUBMISSION_ITEM]: SubmissionItem,
[BoardNodeType.VIDEO_CONFERENCE_ELEMENT]: VideoConferenceElement,
[BoardNodeType.DELETED_ELEMENT]: DeletedElement,
} as const;

Expand Down
Loading

0 comments on commit 0b4dacc

Please sign in to comment.