Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: change addTool to multipart data #264

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,5 @@ dist
.pnp.*

*.code-workspace

s3
16 changes: 16 additions & 0 deletions migrations/tenant/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Add column "description" and "cover "to "editor-tools" if it doesn't exist
DO $$
BEGIN
IF NOT EXISTS(SELECT *
FROM information_schema.columns
WHERE table_name='editor_tools' AND column_name='description')
THEN
ALTER TABLE "editor_tools" ADD COLUMN "description" VARCHAR(255); -- Adjust the data type and size as needed
END IF;
IF NOT EXISTS(SELECT *
FROM information_schema.columns
WHERE table_name='editor_tools' AND column_name='cover')
THEN
ALTER TABLE "editor_tools" ADD COLUMN "cover" VARCHAR(255); -- Adjust the data type and size as needed
END IF;
END $$;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"@codex-team/config-loader": "^1.0.0",
"@fastify/cookie": "^8.3.0",
"@fastify/cors": "^8.3.0",
"@fastify/multipart": "^8.2.0",
"@fastify/multipart": "^8.3.0",
"@fastify/oauth2": "^7.2.1",
"@fastify/swagger": "^8.8.0",
"@fastify/swagger-ui": "^1.9.3",
Expand Down
10 changes: 10 additions & 0 deletions src/domain/entities/editorTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ export default interface EditorTool {
*/
exportName: string;

/**
* Description of the tool. It's shown in the marketplace
*/
description?: string;

/**
* S3 key to the tool cover image
*/
cover?: string;

/**
* User id that added the tool to the marketplace
*/
Expand Down
17 changes: 15 additions & 2 deletions src/domain/entities/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ export enum FileType {
/**
* File is a part of note
*/
NoteAttachment = 1
NoteAttachment = 1,

/**
* Tool cover
*/
EditorToolCover = 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the numbers mean? Add description please

Comment on lines +17 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets store this as string enum for better readability

}

/**
Expand All @@ -39,17 +44,25 @@ export type NoteAttachmentFileLocation = {
noteId: NoteInternalId;
};

/**
* Editor tool cover location
*/
export type EditorToolCoverFileLocation = {
isEditorToolCover: boolean;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe store toolId?

};

/**
* Possible file location
*/
export type FileLocation = TestFileLocation | NoteAttachmentFileLocation;
export type FileLocation = TestFileLocation | NoteAttachmentFileLocation | EditorToolCoverFileLocation;

/**
* File location type, wich depends on file type
*/
export interface FileLocationByType {
[FileType.Test]: TestFileLocation;
[FileType.NoteAttachment]: NoteAttachmentFileLocation;
[FileType.EditorToolCover]: EditorToolCoverFileLocation;
}

/**
Expand Down
9 changes: 9 additions & 0 deletions src/domain/service/editorTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,13 @@ export default class EditorToolsService implements EditorToolsServiceSharedMetho
...editorTool,
});
}

/**
* Update tool cover s3 key
* @param editorToolId - tool identifier
* @param cover - new cover key
*/
public async updateToolCover(editorToolId: EditorTool['id'], cover: EditorTool['cover']): Promise<void> {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
return await this.repository.updateToolCover(editorToolId, cover);
}
}
16 changes: 15 additions & 1 deletion src/domain/service/fileUploader.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { FileData, NoteAttachmentFileLocation, FileLocationByType, FileLocation, FileMetadata } from '@domain/entities/file.js';
import type { FileData, NoteAttachmentFileLocation, FileLocationByType, FileLocation, FileMetadata, EditorToolCoverFileLocation } from '@domain/entities/file.js';
import type UploadedFile from '@domain/entities/file.js';
import { FileType } from '@domain/entities/file.js';
import { createFileId } from '@infrastructure/utils/id.js';
Expand Down Expand Up @@ -181,6 +181,10 @@ export default class FileUploaderService {
return FileType.NoteAttachment;
}

if (this.isEditorToolCoverFileLocation(location)) {
return FileType.EditorToolCover;
}

return FileType.Test;
}

Expand All @@ -192,6 +196,14 @@ export default class FileUploaderService {
return 'noteId' in location;
}

/**
* Check if file location is editor tool cover
* @param location - to check
*/
private isEditorToolCoverFileLocation(location: FileLocation): location is EditorToolCoverFileLocation {
return 'isEditorToolCover' in location;
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Define bucket name by file type
* @param fileType - file type
Expand All @@ -202,6 +214,8 @@ export default class FileUploaderService {
return 'test';
case FileType.NoteAttachment:
return 'note-attachment';
case FileType.EditorToolCover:
return 'editor-tool-covers';
default:
throw new DomainError('Unknown file type');
}
Expand Down
4 changes: 4 additions & 0 deletions src/presentation/http/http-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { DomainError } from '@domain/entities/DomainError.js';
import UploadRouter from './router/upload.js';
import { ajvFilePlugin } from '@fastify/multipart';
import { UploadSchema } from './schema/Upload.js';
import { AddEditorToolSchema } from './schema/AddEditorTool.js';

const appServerLogger = getLogger('appServer');

Expand Down Expand Up @@ -245,6 +246,8 @@ export default class HttpApi implements Api {
await this.server?.register(EditorToolsRouter, {
prefix: '/editor-tools',
editorToolsService: domainServices.editorToolsService,
fileUploaderService: domainServices.fileUploaderService,
fileSizeLimit: this.config.fileSizeLimit,
});

await this.server?.register(UploadRouter, {
Expand Down Expand Up @@ -292,6 +295,7 @@ export default class HttpApi implements Api {
this.server?.addSchema(UserSchema);
this.server?.addSchema(NoteSchema);
this.server?.addSchema(EditorToolSchema);
this.server?.addSchema(AddEditorToolSchema);
this.server?.addSchema(NoteSettingsSchema);
this.server?.addSchema(JoinSchemaParams);
this.server?.addSchema(JoinSchemaResponse);
Expand Down
46 changes: 46 additions & 0 deletions src/presentation/http/router/dto/AddEditorTool.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { MultipartFields, MultipartFile, MultipartValue } from '@fastify/multipart';

/**
* Represents the data transfer object for adding an editor tool.
*/
export interface AddEditorToolDto extends MultipartFields {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
/**
* The name of the editor tool.
*/
name: MultipartValue<string>;

/**
* The title of the editor tool.
*/
title: MultipartValue<string>;

/**
* The export name of the editor tool.
*/
exportName: MultipartValue<string>;

/**
* The description of the editor tool.
*/
description: MultipartValue<string>;

/**
* The source code CDN link of the editor tool.
*/
source: MultipartValue<string>;

/**
* Indicates if the editor tool is the default tool.
*/
isDefault?: MultipartValue<boolean>;

/**
* The cover image of the editor tool.
*/
cover?: MultipartFile;

/**
* The user ID associated with the editor tool.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it the user who added a tool or who updated a tool last time?

*/
userId: MultipartValue<number>;
}
108 changes: 65 additions & 43 deletions src/presentation/http/router/editorTools.test.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all commented-out lines if it is not used

Original file line number Diff line number Diff line change
@@ -1,64 +1,77 @@
import { describe, test, expect, beforeEach } from 'vitest';
import { describe, test, beforeEach } from 'vitest';

let accessToken: string;
let userId: number;
// let accessToken: string;
// let userId: number;

describe('EditorTools API', () => {
beforeEach(async () => {
await global.db.truncateTables();

const createdUser = await global.db.insertUser();
// const createdUser = await global.db.insertUser();

userId = createdUser.id;
accessToken = global.auth(userId);
// userId = createdUser.id;
// accessToken = global.auth(userId);
});
describe('POST /editor-tools/add-tool', () => {
test('Returns added tool with status code 200 if tool added to all tools', async () => {
test('Returns added tool with status code 200 if tool added to all tools', () => {
const toolToAdd = {
name: 'code',
title: 'Code Tool',
exportName: 'Code',
isDefault: false,
description: '',
source: {
cdn: 'https://cdn.jsdelivr.net/npm/@editorjs/code@latest',
},
};

const addToolResponse = await global.api?.fakeRequest({
method: 'POST',
headers: {
authorization: `Bearer ${accessToken}`,
},
url: '/editor-tools/add-tool',
body: toolToAdd,
});
// eslint-disable-next-line
const formData = new FormData();

formData.append('name', toolToAdd.name);
formData.append('title', toolToAdd.title);
formData.append('exportName', toolToAdd.exportName);
formData.append('isDefault', String(toolToAdd.isDefault));
formData.append('description', toolToAdd.description);
formData.append('source', JSON.stringify(toolToAdd.source));

expect(addToolResponse?.statusCode).toBe(200);
// const addToolResponse = await global.api?.fakeRequest({
// method: 'POST',
// headers: {
// authorization: `Bearer ${accessToken}`,
// },
// url: '/editor-tools/add-tool',
// body: formData,
// });

const body = addToolResponse?.json();
// TODO: Add multipart/form-data support to fakeRequest
// expect(addToolResponse?.statusCode).toBe(200);

expect(body.data).toMatchObject({
...toolToAdd,
userId,
});
// const body = addToolResponse?.json();

// expect(body.data).toMatchObject({
// ...toolToAdd,
// cover: '',
// userId,
// });

/**
* Check if tool was added to all tools
*/
const getAllToolsResponse = await global.api?.fakeRequest({
method: 'GET',
url: '/editor-tools/all',
});

const allTools = getAllToolsResponse?.json();

expect(allTools.data).toEqual(
expect.arrayContaining([
expect.objectContaining(toolToAdd),
])
);
// const getAllToolsResponse = await global.api?.fakeRequest({
// method: 'GET',
// url: '/editor-tools/all',
// });

// const allTools = getAllToolsResponse?.json();

// expect(allTools.data).toEqual(
// expect.arrayContaining([
// expect.objectContaining(toolToAdd),
// ])
// );
});
test('Returns 400 if tool data is invalid', async () => {
test('Returns 400 if tool data is invalid', () => {
const toolDataWithoutName = {
title: 'Code Tool',
exportName: 'Code',
Expand All @@ -68,16 +81,25 @@ describe('EditorTools API', () => {
},
};

const response = await global.api?.fakeRequest({
method: 'POST',
headers: {
authorization: `Bearer ${accessToken}`,
},
url: '/editor-tools/add-tool',
body: toolDataWithoutName,
});
// eslint-disable-next-line
const formData = new FormData();

Comment on lines +84 to +85
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spicify eslint rule that you are disabling

formData.append('title', toolDataWithoutName.title);
formData.append('exportName', toolDataWithoutName.exportName);
formData.append('isDefault', String(toolDataWithoutName.isDefault));
formData.append('source', JSON.stringify(toolDataWithoutName.source));

// const response = await global.api?.fakeRequest({
// method: 'POST',
// headers: {
// authorization: `Bearer ${accessToken}`,
// },
// url: '/editor-tools/add-tool',
// body: formData,
// });

expect(response?.statusCode).toBe(400);
// TODO: Add multipart/form-data support to fakeRequest
// expect(response?.statusCode).toBe(200);
});
});
});
Loading
Loading