Skip to content

Commit

Permalink
Solve merging tldraw modules
Browse files Browse the repository at this point in the history
  • Loading branch information
blazejpass committed Sep 25, 2023
2 parents 0bb1ee9 + 97f5b6c commit 91f59f4
Show file tree
Hide file tree
Showing 42 changed files with 649 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
UserAndAccountTestFactory,
} from '@shared/testing';
import { ServerTestModule } from '@src/modules/server/server.module';
import { AnyContentElementResponse } from '../dto';
import { AnyContentElementResponse, SubmissionContainerElementResponse } from '../dto';

const baseRouteName = '/cards';

Expand Down Expand Up @@ -91,14 +91,15 @@ describe(`content element create (api)`, () => {
expect((response.body as AnyContentElementResponse).type).toEqual(ContentElementType.EXTERNAL_TOOL);
});

it('should return the created content element of type SUBMISSION_CONTAINER', async () => {
it('should return the created content element of type SUBMISSION_CONTAINER with dueDate set to null', async () => {
const { loggedInClient, cardNode } = await setup();

const response = await loggedInClient.post(`${cardNode.id}/elements`, {
type: ContentElementType.SUBMISSION_CONTAINER,
});

expect((response.body as AnyContentElementResponse).type).toEqual(ContentElementType.SUBMISSION_CONTAINER);
expect((response.body as SubmissionContainerElementResponse).content.dueDate).toBeNull();
});

it('should actually create the content element', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import {
FileElementNode,
InputFormat,
RichTextElementNode,
SubmissionContainerElementNode,
} from '@shared/domain';
import {
TestApiClient,
UserAndAccountTestFactory,
cardNodeFactory,
cleanupCollections,
columnBoardNodeFactory,
columnNodeFactory,
courseFactory,
fileElementNodeFactory,
richTextElementNodeFactory,
submissionContainerElementNodeFactory,
TestApiClient,
UserAndAccountTestFactory,
} from '@shared/testing';
import { ServerTestModule } from '@src/modules/server/server.module';

Expand Down Expand Up @@ -59,29 +61,44 @@ describe(`content element update content (api)`, () => {

const column = columnNodeFactory.buildWithId({ parent: columnBoardNode });
const parentCard = cardNodeFactory.buildWithId({ parent: column });
const richTextelement = richTextElementNodeFactory.buildWithId({ parent: parentCard });
const richTextElement = richTextElementNodeFactory.buildWithId({ parent: parentCard });
const fileElement = fileElementNodeFactory.buildWithId({ parent: parentCard });
const submissionContainerElement = submissionContainerElementNodeFactory.buildWithId({ parent: parentCard });

const tomorrow = new Date(Date.now() + 86400000);
const submissionContainerElementWithDueDate = submissionContainerElementNodeFactory.buildWithId({
parent: parentCard,
dueDate: tomorrow,
});

await em.persistAndFlush([
teacherAccount,
teacherUser,
parentCard,
column,
columnBoardNode,
richTextelement,
richTextElement,
fileElement,
submissionContainerElement,
submissionContainerElementWithDueDate,
]);
em.clear();

const loggedInClient = await testApiClient.login(teacherAccount);

return { loggedInClient, richTextelement, fileElement };
return {
loggedInClient,
richTextElement,
fileElement,
submissionContainerElement,
submissionContainerElementWithDueDate,
};
};

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

const response = await loggedInClient.patch(`${richTextelement.id}/content`, {
const response = await loggedInClient.patch(`${richTextElement.id}/content`, {
data: {
content: { text: 'hello world', inputFormat: InputFormat.RICH_TEXT_CK5 },
type: ContentElementType.RICH_TEXT,
Expand All @@ -92,30 +109,30 @@ describe(`content element update content (api)`, () => {
});

it('should actually change content of the element', async () => {
const { loggedInClient, richTextelement } = await setup();
const { loggedInClient, richTextElement } = await setup();

await loggedInClient.patch(`${richTextelement.id}/content`, {
await loggedInClient.patch(`${richTextElement.id}/content`, {
data: {
content: { text: 'hello world', inputFormat: InputFormat.RICH_TEXT_CK5 },
type: ContentElementType.RICH_TEXT,
},
});
const result = await em.findOneOrFail(RichTextElementNode, richTextelement.id);
const result = await em.findOneOrFail(RichTextElementNode, richTextElement.id);

expect(result.text).toEqual('hello world');
});

it('should sanitize rich text before changing content of the element', async () => {
const { loggedInClient, richTextelement } = await setup();
const { loggedInClient, richTextElement } = await setup();

const text = '<iframe>rich text 1</iframe> some more text';

const sanitizedText = sanitizeRichText(text, InputFormat.RICH_TEXT_CK5);

await loggedInClient.patch(`${richTextelement.id}/content`, {
await loggedInClient.patch(`${richTextElement.id}/content`, {
data: { content: { text, inputFormat: InputFormat.RICH_TEXT_CK5 }, type: ContentElementType.RICH_TEXT },
});
const result = await em.findOneOrFail(RichTextElementNode, richTextelement.id);
const result = await em.findOneOrFail(RichTextElementNode, richTextElement.id);

expect(result.text).toEqual(sanitizedText);
});
Expand Down Expand Up @@ -146,6 +163,76 @@ 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 () => {
const { loggedInClient, submissionContainerElement } = await setup();

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

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

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

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

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

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

const inThreeDays = new Date(Date.now() + 259200000);

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

expect(result.dueDate).toEqual(inThreeDays);
});

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

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

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

it('should return status 400 for wrong date format for submission container element', async () => {
const { loggedInClient, submissionContainerElement } = await setup();

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

expect(response.statusCode).toEqual(400);
});
});

describe('with invalid user', () => {
Expand All @@ -163,24 +250,38 @@ describe(`content element update content (api)`, () => {

const column = columnNodeFactory.buildWithId({ parent: columnBoardNode });
const parentCard = cardNodeFactory.buildWithId({ parent: column });
const element = richTextElementNodeFactory.buildWithId({ parent: parentCard });
const richTextElement = richTextElementNodeFactory.buildWithId({ parent: parentCard });
const submissionContainerElement = submissionContainerElementNodeFactory.buildWithId({ parent: parentCard });

await em.persistAndFlush([parentCard, column, columnBoardNode, element]);
await em.persistAndFlush([parentCard, column, columnBoardNode, richTextElement, submissionContainerElement]);
em.clear();

const loggedInClient = await testApiClient.login(invalidTeacherAccount);

return { loggedInClient, element };
return { loggedInClient, richTextElement, submissionContainerElement };
};

it('should return status 403', async () => {
const { loggedInClient, element } = await setup();
it('should return status 403 for rich text element', async () => {
const { loggedInClient, richTextElement } = await setup();

const response = await loggedInClient.patch(`${element.id}/content`, {
const response = await loggedInClient.patch(`${richTextElement.id}/content`, {
data: { content: { text: 'hello world', inputFormat: InputFormat.RICH_TEXT_CK5 }, type: 'richText' },
});

expect(response.statusCode).toEqual(HttpStatus.FORBIDDEN);
});

it('should return status 403 for submission container element', async () => {
const { loggedInClient, submissionContainerElement } = await setup();

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

expect(response.statusCode).toEqual(HttpStatus.FORBIDDEN);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
userFactory,
} from '@shared/testing';
import { ServerTestModule } from '@src/modules/server';
import { SubmissionsResponse } from '../dto';
import { SubmissionsResponse } from '../dto/submission-item/submissions.response';

const baseRouteName = '/board-submissions';
describe('submission item lookup (api)', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger';
import { DecodeHtmlEntities } from '@shared/controller';
import { AnyContentElementResponse } from '../element';
import { AnyContentElementResponse, FileElementResponse, SubmissionContainerElementResponse } from '../element';
import { RichTextElementResponse } from '../element/rich-text-element.response';
import { TimestampsResponse } from '../timestamps.response';
import { VisibilitySettingsResponse } from './visibility-settings.response';
Expand Down Expand Up @@ -31,7 +31,11 @@ export class CardResponse {
@ApiProperty({
type: 'array',
items: {
oneOf: [{ $ref: getSchemaPath(RichTextElementResponse) }],
oneOf: [
{ $ref: getSchemaPath(RichTextElementResponse) },
{ $ref: getSchemaPath(FileElementResponse) },
{ $ref: getSchemaPath(SubmissionContainerElementResponse) },
],
},
})
elements: AnyContentElementResponse[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ export class SubmissionContainerElementContent {
this.dueDate = dueDate;
}

@ApiProperty()
dueDate: Date;
@ApiProperty({
type: Date,
description: 'The dueDate as date string or null of not set',
example: '2023-08-17T14:17:51.958+00:00',
})
dueDate: Date | null;
}

export class SubmissionContainerElementResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ export class DrawingElementContentBody extends ElementContentBody {

export class SubmissionContainerContentBody {
@IsDate()
@ApiProperty()
dueDate!: Date;
@IsOptional()
@ApiPropertyOptional({
required: false,
description: 'The point in time until when a submission can be handed in.',
})
dueDate?: Date;
}

export class SubmissionContainerElementContentBody extends ElementContentBody {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ export * from './submission-container.url.params';
export * from './create-submission-item.body.params';
export * from './submission-item.response';
export * from './submission-item.url.params';
export * from './submissions.response';
// TODO for some reason, api generator messes up the types
// import it directly, not via this index seems to fix it
// export * from './submissions.response';
export * from './update-submission-item.body.params';
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ export class SubmissionContainerElementResponseMapper implements BaseResponseMap
id: element.id,
timestamps: new TimestampsResponse({ lastUpdatedAt: element.updatedAt, createdAt: element.createdAt }),
type: ContentElementType.SUBMISSION_CONTAINER,
content: new SubmissionContainerElementContent({ dueDate: element.dueDate }),
content: new SubmissionContainerElementContent({
dueDate: element.dueDate || null,
}),
});

if (element.dueDate) {
result.content = new SubmissionContainerElementContent({ dueDate: element.dueDate });
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SubmissionItem, UserBoardRoles } from '@shared/domain';
import { SubmissionItemResponse, SubmissionsResponse, TimestampsResponse, UserDataResponse } from '../dto';
import { SubmissionsResponse } from '../dto/submission-item/submissions.response';
import { SubmissionItemResponse, TimestampsResponse, UserDataResponse } from '../dto';

export class SubmissionItemResponseMapper {
private static instance: SubmissionItemResponseMapper;
Expand Down
6 changes: 5 additions & 1 deletion apps/server/src/modules/board/repo/board-do.builder-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,15 @@ export class BoardDoBuilderImpl implements BoardDoBuilder {

const element = new SubmissionContainerElement({
id: boardNode.id,
dueDate: boardNode.dueDate,
children: elements,
createdAt: boardNode.createdAt,
updatedAt: boardNode.updatedAt,
});

if (boardNode.dueDate) {
element.dueDate = boardNode.dueDate;
}

return element;
}

Expand Down
5 changes: 4 additions & 1 deletion apps/server/src/modules/board/repo/recursive-save.visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,14 @@ export class RecursiveSaveVisitor implements BoardCompositeVisitor {

const boardNode = new SubmissionContainerElementNode({
id: submissionContainerElement.id,
dueDate: submissionContainerElement.dueDate,
parent: parentData?.boardNode,
position: parentData?.position,
});

if (submissionContainerElement.dueDate) {
boardNode.dueDate = submissionContainerElement.dueDate;
}

this.createOrUpdateBoardNode(boardNode);
this.visitChildren(submissionContainerElement, boardNode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class ContentElementUpdateVisitor implements BoardCompositeVisitor {

visitSubmissionContainerElement(submissionContainerElement: SubmissionContainerElement): void {
if (this.content instanceof SubmissionContainerContentBody) {
submissionContainerElement.dueDate = this.content.dueDate;
submissionContainerElement.dueDate = this.content.dueDate ?? undefined;
} else {
this.throwNotHandled(submissionContainerElement);
}
Expand Down
Loading

0 comments on commit 91f59f4

Please sign in to comment.