Skip to content

Commit

Permalink
Merge branch 'main' into N21-939-assign-groups-to-course
Browse files Browse the repository at this point in the history
  • Loading branch information
mrikallab committed Oct 12, 2023
2 parents 1192880 + a8f7cda commit 95fd873
Show file tree
Hide file tree
Showing 62 changed files with 1,581 additions and 165 deletions.
2 changes: 2 additions & 0 deletions apps/server/src/modules/board/board.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ColumnBoardService,
ColumnService,
ContentElementService,
OpenGraphProxyService,
SubmissionItemService,
} from './service';
import { BoardDoCopyService, SchoolSpecificFileCopyServiceFactory } from './service/board-do-copy-service';
Expand All @@ -37,6 +38,7 @@ import { ColumnBoardCopyService } from './service/column-board-copy.service';
BoardDoCopyService,
ColumnBoardCopyService,
SchoolSpecificFileCopyServiceFactory,
OpenGraphProxyService,
],
exports: [
BoardDoAuthorizableService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe(`card create (api)`, () => {
em.clear();

const createCardBodyParams = {
requiredEmptyElements: [ContentElementType.RICH_TEXT, ContentElementType.FILE],
requiredEmptyElements: [ContentElementType.RICH_TEXT, ContentElementType.FILE, ContentElementType.LINK],
};

return { user, columnBoardNode, columnNode, createCardBodyParams };
Expand All @@ -111,7 +111,7 @@ describe(`card create (api)`, () => {

expect(result.id).toBeDefined();
});
it('created card should contain empty text and file elements', async () => {
it('created card should contain empty text, file and link elements', async () => {
const { user, columnNode, createCardBodyParams } = await setup();
currentUser = mapUserToCurrentUser(user);

Expand All @@ -129,13 +129,20 @@ describe(`card create (api)`, () => {
alternativeText: '',
},
},
{
type: 'link',
content: {
url: '',
},
},
];

const { result } = await api.post(columnNode.id, createCardBodyParams);
const { elements } = result;

expect(elements[0]).toMatchObject(expectedEmptyElements[0]);
expect(elements[1]).toMatchObject(expectedEmptyElements[1]);
expect(elements[2]).toMatchObject(expectedEmptyElements[2]);
});
it('should return status 400 as the content element is unknown', async () => {
const { user, columnNode } = await setup();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
SubmissionContainerElementNode,
} from '@shared/domain';
import {
TestApiClient,
UserAndAccountTestFactory,
cardNodeFactory,
cleanupCollections,
columnBoardNodeFactory,
Expand All @@ -19,8 +21,6 @@ import {
fileElementNodeFactory,
richTextElementNodeFactory,
submissionContainerElementNodeFactory,
TestApiClient,
UserAndAccountTestFactory,
} from '@shared/testing';
import { ServerTestModule } from '@src/modules/server/server.module';

Expand Down Expand Up @@ -63,7 +63,10 @@ describe(`content element update content (api)`, () => {
const parentCard = cardNodeFactory.buildWithId({ parent: column });
const richTextElement = richTextElementNodeFactory.buildWithId({ parent: parentCard });
const fileElement = fileElementNodeFactory.buildWithId({ parent: parentCard });
const submissionContainerElement = submissionContainerElementNodeFactory.buildWithId({ parent: parentCard });
const submissionContainerElement = submissionContainerElementNodeFactory.buildWithId({
parent: parentCard,
dueDate: null,
});

const tomorrow = new Date(Date.now() + 86400000);
const submissionContainerElementWithDueDate = submissionContainerElementNodeFactory.buildWithId({
Expand Down Expand Up @@ -95,7 +98,7 @@ describe(`content element update content (api)`, () => {
};
};

it('should return status 204', async () => {
it('should return status 201', async () => {
const { loggedInClient, richTextElement } = await setup();

const response = await loggedInClient.patch(`${richTextElement.id}/content`, {
Expand All @@ -105,7 +108,7 @@ describe(`content element update content (api)`, () => {
},
});

expect(response.statusCode).toEqual(204);
expect(response.statusCode).toEqual(201);
});

it('should actually change content of the element', async () => {
Expand Down Expand Up @@ -164,20 +167,19 @@ describe(`content element update content (api)`, () => {
expect(result.alternativeText).toEqual('rich text 1 some more text');
});

it('should return status 204 (nothing changed) without dueDate parameter for submission container element', async () => {
it('should return status 201', async () => {
const { loggedInClient, submissionContainerElement } = await setup();

const response = await loggedInClient.patch(`${submissionContainerElement.id}/content`, {
data: {
content: {},
type: 'submissionContainer',
},
});

expect(response.statusCode).toEqual(204);
expect(response.statusCode).toEqual(201);
});

it('should not change dueDate value without dueDate parameter for submission container element', async () => {
it('should not change dueDate when not proviced in submission container element without dueDate', async () => {
const { loggedInClient, submissionContainerElement } = await setup();

await loggedInClient.patch(`${submissionContainerElement.id}/content`, {
Expand All @@ -187,11 +189,10 @@ describe(`content element update content (api)`, () => {
},
});
const result = await em.findOneOrFail(SubmissionContainerElementNode, submissionContainerElement.id);

expect(result.dueDate).toBeUndefined();
expect(result.dueDate).toBeNull();
});

it('should set dueDate value when dueDate parameter is provided for submission container element', async () => {
it('should set dueDate value when provided for submission container element', async () => {
const { loggedInClient, submissionContainerElement } = await setup();

const inThreeDays = new Date(Date.now() + 259200000);
Expand All @@ -207,18 +208,20 @@ describe(`content element update content (api)`, () => {
expect(result.dueDate).toEqual(inThreeDays);
});

it('should unset dueDate value when dueDate parameter is not provided for submission container element', async () => {
it('should unset dueDate value when dueDate parameter is null for submission container element', async () => {
const { loggedInClient, submissionContainerElementWithDueDate } = await setup();

await loggedInClient.patch(`${submissionContainerElementWithDueDate.id}/content`, {
data: {
content: {},
content: {
dueDate: null,
},
type: 'submissionContainer',
},
});
const result = await em.findOneOrFail(SubmissionContainerElementNode, submissionContainerElementWithDueDate.id);

expect(result.dueDate).toBeUndefined();
expect(result.dueDate).toBeNull();
});

it('should return status 400 for wrong date format for submission container element', async () => {
Expand Down
15 changes: 9 additions & 6 deletions apps/server/src/modules/board/controller/card.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
CreateContentElementBodyParams,
ExternalToolElementResponse,
FileElementResponse,
LinkElementResponse,
MoveCardBodyParams,
RenameBodyParams,
RichTextElementResponse,
Expand Down Expand Up @@ -116,19 +117,21 @@ export class CardController {

@ApiOperation({ summary: 'Create a new element on a card.' })
@ApiExtraModels(
RichTextElementResponse,
ExternalToolElementResponse,
FileElementResponse,
SubmissionContainerElementResponse,
ExternalToolElementResponse
LinkElementResponse,
RichTextElementResponse,
SubmissionContainerElementResponse
)
@ApiResponse({
status: 201,
schema: {
oneOf: [
{ $ref: getSchemaPath(RichTextElementResponse) },
{ $ref: getSchemaPath(ExternalToolElementResponse) },
{ $ref: getSchemaPath(FileElementResponse) },
{ $ref: getSchemaPath(LinkElementResponse) },
{ $ref: getSchemaPath(RichTextElementResponse) },
{ $ref: getSchemaPath(SubmissionContainerElementResponse) },
{ $ref: getSchemaPath(ExternalToolElementResponse) },
],
},
})
Expand All @@ -137,7 +140,7 @@ export class CardController {
@ApiResponse({ status: 404, type: NotFoundException })
@Post(':cardId/elements')
async createElement(
@Param() urlParams: CardUrlParams, // TODO add type-property ?
@Param() urlParams: CardUrlParams,
@Body() bodyParams: CreateContentElementBodyParams,
@CurrentUser() currentUser: ICurrentUser
): Promise<AnyContentElementResponse> {
Expand Down
22 changes: 18 additions & 4 deletions apps/server/src/modules/board/controller/dto/card/card.response.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger';
import { DecodeHtmlEntities } from '@shared/controller';
import { AnyContentElementResponse, FileElementResponse, SubmissionContainerElementResponse } from '../element';
import { RichTextElementResponse } from '../element/rich-text-element.response';
import {
AnyContentElementResponse,
ExternalToolElementResponse,
FileElementResponse,
LinkElementResponse,
RichTextElementResponse,
SubmissionContainerElementResponse,
} from '../element';
import { TimestampsResponse } from '../timestamps.response';
import { VisibilitySettingsResponse } from './visibility-settings.response';

@ApiExtraModels(RichTextElementResponse)
@ApiExtraModels(
ExternalToolElementResponse,
FileElementResponse,
LinkElementResponse,
RichTextElementResponse,
SubmissionContainerElementResponse
)
export class CardResponse {
constructor({ id, title, height, elements, visibilitySettings, timestamps }: CardResponse) {
this.id = id;
Expand All @@ -32,8 +44,10 @@ export class CardResponse {
type: 'array',
items: {
oneOf: [
{ $ref: getSchemaPath(RichTextElementResponse) },
{ $ref: getSchemaPath(ExternalToolElementResponse) },
{ $ref: getSchemaPath(FileElementResponse) },
{ $ref: getSchemaPath(LinkElementResponse) },
{ $ref: getSchemaPath(RichTextElementResponse) },
{ $ref: getSchemaPath(SubmissionContainerElementResponse) },
],
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ExternalToolElementResponse } from './external-tool-element.response';
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';

export type AnyContentElementResponse =
| FileElementResponse
| LinkElementResponse
| RichTextElementResponse
| SubmissionContainerElementResponse
| ExternalToolElementResponse;
5 changes: 3 additions & 2 deletions apps/server/src/modules/board/controller/dto/element/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export * from './any-content-element.response';
export * from './create-content-element.body.params';
export * from './update-element-content.body.params';
export * from './external-tool-element.response';
export * from './file-element.response';
export * from './link-element.response';
export * from './rich-text-element.response';
export * from './submission-container-element.response';
export * from './external-tool-element.response';
export * from './update-element-content.body.params';
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { ContentElementType } from '@shared/domain';
import { TimestampsResponse } from '../timestamps.response';

export class LinkElementContent {
constructor({ url, title, description, imageUrl }: LinkElementContent) {
this.url = url;
this.title = title;
this.description = description;
this.imageUrl = imageUrl;
}

@ApiProperty()
url: string;

@ApiProperty()
title: string;

@ApiPropertyOptional()
description?: string;

@ApiPropertyOptional()
imageUrl?: string;
}

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

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

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

@ApiProperty()
content: LinkElementContent;

@ApiProperty()
timestamps: TimestampsResponse;
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ export class FileElementContentBody extends ElementContentBody {
@ApiProperty()
content!: FileContentBody;
}
export class LinkContentBody {
@IsString()
@ApiProperty({})
url!: string;
}

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

@ValidateNested()
@ApiProperty({})
content!: LinkContentBody;
}

export class RichTextContentBody {
@IsString()
Expand All @@ -56,6 +70,7 @@ export class SubmissionContainerContentBody {
@IsOptional()
@ApiPropertyOptional({
required: false,
nullable: true,
description: 'The point in time until when a submission can be handed in.',
})
dueDate?: Date;
Expand Down Expand Up @@ -88,6 +103,7 @@ export class ExternalToolElementContentBody extends ElementContentBody {

export type AnyElementContentBody =
| FileContentBody
| LinkContentBody
| RichTextContentBody
| SubmissionContainerContentBody
| ExternalToolContentBody;
Expand All @@ -99,6 +115,7 @@ export class UpdateElementContentBodyParams {
property: 'type',
subTypes: [
{ value: FileElementContentBody, name: ContentElementType.FILE },
{ value: LinkElementContentBody, name: ContentElementType.LINK },
{ value: RichTextElementContentBody, name: ContentElementType.RICH_TEXT },
{ value: SubmissionContainerElementContentBody, name: ContentElementType.SUBMISSION_CONTAINER },
{ value: ExternalToolElementContentBody, name: ContentElementType.EXTERNAL_TOOL },
Expand All @@ -109,13 +126,15 @@ export class UpdateElementContentBodyParams {
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(FileElementContentBody) },
{ $ref: getSchemaPath(LinkElementContentBody) },
{ $ref: getSchemaPath(RichTextElementContentBody) },
{ $ref: getSchemaPath(SubmissionContainerElementContentBody) },
{ $ref: getSchemaPath(ExternalToolElementContentBody) },
],
})
data!:
| FileElementContentBody
| LinkElementContentBody
| RichTextElementContentBody
| SubmissionContainerElementContentBody
| ExternalToolElementContentBody;
Expand Down
Loading

0 comments on commit 95fd873

Please sign in to comment.