Skip to content

Commit

Permalink
N21-1507 copy ctl tools (#4681)
Browse files Browse the repository at this point in the history
* copy ctl tools in tab and board when course copy
* change validation of context and school external tool

---------

Co-authored-by: Marvin Öhlerking <[email protected]>
Co-authored-by: Arne Gnisa <[email protected]>
  • Loading branch information
3 people authored Jan 12, 2024
1 parent 003e7d4 commit dbd2e3f
Show file tree
Hide file tree
Showing 76 changed files with 2,341 additions and 1,168 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 @@ -8,6 +8,7 @@ import { CourseRepo } from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import { DrawingElementAdapterService } from '@modules/tldraw-client/service/drawing-element-adapter.service';
import { HttpModule } from '@nestjs/axios';
import { ToolConfigModule } from '@modules/tool/tool-config.module';
import { BoardDoRepo, BoardNodeRepo, RecursiveDeleteVisitor } from './repo';
import {
BoardDoAuthorizableService,
Expand All @@ -29,6 +30,7 @@ import { ColumnBoardCopyService } from './service/column-board-copy.service';
UserModule,
ContextExternalToolModule,
HttpModule,
ToolConfigModule,
],
providers: [
BoardDoAuthorizableService,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { createMock } from '@golevelup/ts-jest';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { FileRecordParentType } from '@infra/rabbitmq';
import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper';
import { ContextExternalTool } from '@modules/tool/context-external-tool/domain';
import { ContextExternalToolService } from '@modules/tool/context-external-tool/service';
import { Test, TestingModule } from '@nestjs/testing';
import {
Card,
Expand All @@ -9,9 +11,6 @@ import {
DrawingElement,
ExternalToolElement,
FileElement,
LinkElement,
RichTextElement,
SubmissionContainerElement,
isCard,
isColumn,
isColumnBoard,
Expand All @@ -21,11 +20,15 @@ import {
isLinkElement,
isRichTextElement,
isSubmissionContainerElement,
LinkElement,
RichTextElement,
SubmissionContainerElement,
} from '@shared/domain/domainobject';
import {
cardFactory,
columnBoardFactory,
columnFactory,
contextExternalToolFactory,
drawingElementFactory,
externalToolElementFactory,
fileElementFactory,
Expand All @@ -36,23 +39,43 @@ import {
submissionItemFactory,
} from '@shared/testing';
import { ObjectId } from 'bson';
import { ToolFeatures } from '@modules/tool/tool-config';
import { BoardDoCopyService } from './board-do-copy.service';
import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface';

describe('recursive board copy visitor', () => {
let module: TestingModule;
let service: BoardDoCopyService;

let contextExternalToolService: DeepMocked<ContextExternalToolService>;

beforeAll(async () => {
module = await Test.createTestingModule({
providers: [BoardDoCopyService],
providers: [
BoardDoCopyService,
{
provide: ContextExternalToolService,
useValue: createMock<ContextExternalToolService>(),
},
{
provide: ToolFeatures,
useValue: {
ctlToolsCopyEnabled: true,
},
},
],
}).compile();

service = module.get(BoardDoCopyService);
contextExternalToolService = module.get(ContextExternalToolService);

await setupEntities();
});

afterEach(() => {
jest.clearAllMocks();
});

const setupfileCopyService = () => {
const fileCopyService = createMock<SchoolSpecificFileCopyService>();

Expand Down Expand Up @@ -711,61 +734,192 @@ describe('recursive board copy visitor', () => {
});
});

describe('when copying a external tool element', () => {
const setup = () => {
const original = externalToolElementFactory.build();
describe('when copying an external tool element', () => {
describe('when the element has no linked tool', () => {
const setup = () => {
const original = externalToolElementFactory.build();

return { original, ...setupfileCopyService() };
};
return { original, ...setupfileCopyService() };
};

const getExternalToolElementFromStatus = (status: CopyStatus): ExternalToolElement => {
const copy = status.copyEntity;
const getExternalToolElementFromStatus = (status: CopyStatus): ExternalToolElement => {
const copy = status.copyEntity;

expect(isExternalToolElement(copy)).toEqual(true);
expect(isExternalToolElement(copy)).toEqual(true);

return copy as ExternalToolElement;
};
return copy as ExternalToolElement;
};

it('should return a external tool element as copy', async () => {
const { original, fileCopyService } = setup();
it('should return a external tool element as copy', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });
const result = await service.copy({ original, fileCopyService });

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

it('should not copy tool', async () => {
const { original, fileCopyService } = setup();
it('should not copy tool', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);
const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);

expect(copy.contextExternalToolId).toBeUndefined();
});
expect(copy.contextExternalToolId).toBeUndefined();
});

it('should create new id', async () => {
const { original, fileCopyService } = setup();
it('should create new id', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);
const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);

expect(copy.id).not.toEqual(original.id);
});
expect(copy.id).not.toEqual(original.id);
});

it('should show status successful', async () => {
const { original, fileCopyService } = setup();
it('should show status successful', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });
const result = await service.copy({ original, fileCopyService });

expect(result.status).toEqual(CopyStatusEnum.SUCCESS);
expect(result.status).toEqual(CopyStatusEnum.SUCCESS);
});

it('should show type ExternalToolElement', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

expect(result.type).toEqual(CopyElementType.EXTERNAL_TOOL_ELEMENT);
});
});

it('should show type RichTextElement', async () => {
const { original, fileCopyService } = setup();
describe('when the element has a linked tool and the feature is active', () => {
describe('when the linked tool exists', () => {
const setup = () => {
const originalTool: ContextExternalTool = contextExternalToolFactory.buildWithId();
const copiedTool: ContextExternalTool = contextExternalToolFactory.buildWithId();

const result = await service.copy({ original, fileCopyService });
const original: ExternalToolElement = externalToolElementFactory.build({
contextExternalToolId: originalTool.id,
});

contextExternalToolService.findById.mockResolvedValueOnce(originalTool);
contextExternalToolService.copyContextExternalTool.mockResolvedValueOnce(copiedTool);

return { original, ...setupfileCopyService(), copiedTool };
};

const getExternalToolElementFromStatus = (status: CopyStatus): ExternalToolElement => {
const copy = status.copyEntity;

expect(isExternalToolElement(copy)).toEqual(true);

return copy as ExternalToolElement;
};

it('should return a external tool element as copy', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

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

it('should copy tool', async () => {
const { original, fileCopyService, copiedTool } = setup();

const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);

expect(result.type).toEqual(CopyElementType.EXTERNAL_TOOL_ELEMENT);
expect(copy.contextExternalToolId).toEqual(copiedTool.id);
});

it('should create new id', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);

expect(copy.id).not.toEqual(original.id);
});

it('should show status successful', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

expect(result.status).toEqual(CopyStatusEnum.SUCCESS);
});

it('should show type ExternalToolElement', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

expect(result.type).toEqual(CopyElementType.EXTERNAL_TOOL_ELEMENT);
});
});

describe('when the linked tool does not exist anymore', () => {
const setup = () => {
const original: ExternalToolElement = externalToolElementFactory.build({
contextExternalToolId: new ObjectId().toHexString(),
});

contextExternalToolService.findById.mockResolvedValueOnce(null);

return { original, ...setupfileCopyService() };
};

const getExternalToolElementFromStatus = (status: CopyStatus): ExternalToolElement => {
const copy = status.copyEntity;

expect(isExternalToolElement(copy)).toEqual(true);

return copy as ExternalToolElement;
};

it('should return a external tool element as copy', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

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

it('should not try to copy the tool', async () => {
const { original, fileCopyService } = setup();

await service.copy({ original, fileCopyService });

expect(contextExternalToolService.copyContextExternalTool).not.toHaveBeenCalled();
});

it('should create new id', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });
const copy = getExternalToolElementFromStatus(result);

expect(copy.id).not.toEqual(original.id);
});

it('should show status fail', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

expect(result.status).toEqual(CopyStatusEnum.FAIL);
});

it('should show type ExternalToolElement', async () => {
const { original, fileCopyService } = setup();

const result = await service.copy({ original, fileCopyService });

expect(result.type).toEqual(CopyElementType.EXTERNAL_TOOL_ELEMENT);
});
});
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { ContextExternalToolService } from '@modules/tool/context-external-tool/service';
import { CopyStatus } from '@modules/copy-helper';
import { Injectable } from '@nestjs/common';
import { Inject, Injectable } from '@nestjs/common';
import { AnyBoardDo } from '@shared/domain/domainobject';
import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config';
import { RecursiveCopyVisitor } from './recursive-copy.visitor';
import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface';

Expand All @@ -11,8 +13,17 @@ export type BoardDoCopyParams = {

@Injectable()
export class BoardDoCopyService {
constructor(
@Inject(ToolFeatures) private readonly toolFeatures: IToolFeatures,
private readonly contextExternalToolService: ContextExternalToolService
) {}

public async copy(params: BoardDoCopyParams): Promise<CopyStatus> {
const visitor = new RecursiveCopyVisitor(params.fileCopyService);
const visitor = new RecursiveCopyVisitor(
params.fileCopyService,
this.contextExternalToolService,
this.toolFeatures
);

const result = await visitor.copy(params.original);

Expand Down
Loading

0 comments on commit dbd2e3f

Please sign in to comment.