From ec9e79c08622b065990eb6f5e024aa1f75bab463 Mon Sep 17 00:00:00 2001 From: GeekaN2 Date: Sun, 22 Oct 2023 22:29:28 +0300 Subject: [PATCH 01/40] feat: add default tool to user editor tools --- src/domain/service/editorTools.ts | 9 ++++++- src/presentation/http/router/user.ts | 11 +++++++-- src/repository/editorTools.repository.ts | 7 ++++++ .../postgres/orm/sequelize/editorTools.ts | 24 +++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/domain/service/editorTools.ts b/src/domain/service/editorTools.ts index a25e51d2..c956e0f8 100644 --- a/src/domain/service/editorTools.ts +++ b/src/domain/service/editorTools.ts @@ -32,10 +32,17 @@ export default class EditorToolsService { * * @param editorToolIds - tool ids */ - public async getToolsByIds(editorToolIds: EditorTool['id'][] ): Promise { + public async getToolsByIds(editorToolIds: EditorTool['id'][]): Promise { return await this.repository.getToolsByIds(editorToolIds); } + /** + * Get all default tools + */ + public async getDefaultEditorTools(): Promise { + return await this.repository.getDefaultEditorTools(); + } + /** * Adding custom editor tool * diff --git a/src/presentation/http/router/user.ts b/src/presentation/http/router/user.ts index 2b1e120e..30199607 100644 --- a/src/presentation/http/router/user.ts +++ b/src/presentation/http/router/user.ts @@ -95,10 +95,17 @@ const UserRouter: FastifyPluginCallback = (fastify, opts, don const userExtensions = await userService.getUserExtensions(userId); const userEditorToolIds = userExtensions?.editorTools?.map(tools => tools.id) ?? []; - const editorTools = await editorToolsService.getToolsByIds(userEditorToolIds) ?? []; + + const defaultEditorTools = await editorToolsService.getDefaultEditorTools(); + const uniqueDefaultEditorTools = defaultEditorTools.filter(({ id }) => !userEditorToolIds.includes(id)); + const userEditorTools = await editorToolsService.getToolsByIds(userEditorToolIds) ?? []; + + // Combine user tools and default tools + // TODO: load tools in notes service + const mergedTools = [...userEditorTools, ...uniqueDefaultEditorTools]; return reply.send({ - data: editorTools, + data: mergedTools, }); }); diff --git a/src/repository/editorTools.repository.ts b/src/repository/editorTools.repository.ts index 9a20d359..bbe15e68 100644 --- a/src/repository/editorTools.repository.ts +++ b/src/repository/editorTools.repository.ts @@ -34,6 +34,13 @@ export default class EditorToolsRepository { return tools; } + /** + * Get all default tools + */ + public async getDefaultEditorTools(): Promise { + return await this.storage.getDefaultEditorTools(); + } + /** * Get all editor tools */ diff --git a/src/repository/storage/postgres/orm/sequelize/editorTools.ts b/src/repository/storage/postgres/orm/sequelize/editorTools.ts index f20d3c17..3daad4fa 100644 --- a/src/repository/storage/postgres/orm/sequelize/editorTools.ts +++ b/src/repository/storage/postgres/orm/sequelize/editorTools.ts @@ -31,6 +31,11 @@ export class EditorToolModel extends Model, Inf * Editor tool sources */ public declare source: EditorTool['source']; + + /** + * Applies to user editor tools by default + */ + public declare isDefault: EditorTool['isDefault']; } /** @@ -85,6 +90,10 @@ export default class UserSequelizeStorage { type: DataTypes.JSON, allowNull: false, }, + isDefault: { + type: DataTypes.BOOLEAN, + allowNull: true, + }, }, { tableName: this.tableName, sequelize: this.database, @@ -101,6 +110,7 @@ export default class UserSequelizeStorage { title, exportName, source, + isDefault, }: EditorTool): Promise { const editorTool = await this.model.create({ id, @@ -108,6 +118,7 @@ export default class UserSequelizeStorage { title, exportName, source, + isDefault, }); return editorTool; @@ -130,6 +141,19 @@ export default class UserSequelizeStorage { return editorTools; } + /** + * Get all default tools + */ + public async getDefaultEditorTools(): Promise { + const editorTools = await this.model.findAll({ + where: { + isDefault: true, + }, + }); + + return editorTools; + } + /** * Get all available editor tools */ From 2746ae908a63f88fc7f4cf77c27abdfa9a2298de Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 23 Oct 2023 00:10:10 +0300 Subject: [PATCH 02/40] refactoring: get rid of underscore from code --- ...ers@add-editor-tools-remove-extensions.sql | 22 ++++++++ ...r-tools@rename-camelcase-to-underscore.sql | 24 +++++++++ src/domain/entities/user.ts | 7 ++- src/domain/entities/userExtensions.ts | 19 ------- src/domain/service/user.ts | 19 +++---- src/presentation/http/router/user.ts | 12 ++--- src/presentation/http/schema/User.ts | 7 +++ .../postgres/orm/sequelize/editorTools.ts | 18 ++----- .../storage/postgres/orm/sequelize/index.ts | 6 +++ .../storage/postgres/orm/sequelize/note.ts | 25 ++------- .../postgres/orm/sequelize/noteSettings.ts | 1 - .../storage/postgres/orm/sequelize/user.ts | 53 +++++++------------ .../postgres/orm/sequelize/userSession.ts | 46 ++++++---------- src/repository/user.repository.ts | 20 +++---- 14 files changed, 131 insertions(+), 148 deletions(-) create mode 100644 migrations/tenant/0008-users@add-editor-tools-remove-extensions.sql create mode 100644 migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql delete mode 100644 src/domain/entities/userExtensions.ts diff --git a/migrations/tenant/0008-users@add-editor-tools-remove-extensions.sql b/migrations/tenant/0008-users@add-editor-tools-remove-extensions.sql new file mode 100644 index 00000000..d7168f02 --- /dev/null +++ b/migrations/tenant/0008-users@add-editor-tools-remove-extensions.sql @@ -0,0 +1,22 @@ + +-- Adds "editor_tools" column at "users" if not exists +DO $$ +BEGIN + IF NOT EXISTS(SELECT * + FROM information_schema.columns + WHERE table_name='users' and column_name='editor_tools') + THEN + ALTER TABLE "public"."users" ADD COLUMN "editor_tools" jsonb; + END IF; +END $$; + +-- Removes "extensions" column at "users" if exists +DO $$ +BEGIN + IF EXISTS(SELECT * + FROM information_schema.columns + WHERE table_name='users' and column_name='extensions') + THEN + ALTER TABLE "public"."users" DROP COLUMN "extensions"; + END IF; +END $$; diff --git a/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql b/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql new file mode 100644 index 00000000..7ee601fd --- /dev/null +++ b/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql @@ -0,0 +1,24 @@ +/* + Rename columns in "editor_tools" table: +*/ +-- Rename column "isDefault" to "is_default" if exists +DO $$ +BEGIN + IF EXISTS(SELECT * + FROM information_schema.columns + WHERE table_name='editor_tools' and column_name='isDefault') + THEN + ALTER TABLE "public"."editor_tools" RENAME COLUMN "isDefault" TO "is_default"; + END IF; +END $$; + +-- Rename column "exportName" to "export_name" if exists +DO $$ +BEGIN + IF EXISTS(SELECT * + FROM information_schema.columns + WHERE table_name='editor_tools' and column_name='exportName') + THEN + ALTER TABLE "public"."editor_tools" RENAME COLUMN "exportName" TO "export_name"; + END IF; +END $$; diff --git a/src/domain/entities/user.ts b/src/domain/entities/user.ts index 08a7dc0c..32957ede 100644 --- a/src/domain/entities/user.ts +++ b/src/domain/entities/user.ts @@ -1,4 +1,4 @@ -import type UserExtensions from '@domain/entities/userExtensions.js'; +import type EditorTool from './editorTools'; /** * User entity @@ -30,8 +30,7 @@ export default interface User { photo?: string; /** - * Custom plugins from the marketplace that improve - * editor or notes environment + * Custom plugins ids from the marketplace that improve editor or notes environment */ - extensions?: UserExtensions; + editorTools?: EditorTool['id'][]; } diff --git a/src/domain/entities/userExtensions.ts b/src/domain/entities/userExtensions.ts deleted file mode 100644 index 4ee4acc4..00000000 --- a/src/domain/entities/userExtensions.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type EditorTool from '@domain/entities/editorTools.js'; - -/** - * Tools that user uses in the editor while changing notes - */ -export interface UserEditorTool { - /** - * Unique tool identifier - */ - id: EditorTool['id']; -} - -/** - * Custom user extensions and plugin that expand the capabilities - * of the editor - */ -export default interface UserExtensions { - editorTools?: UserEditorTool[]; -} diff --git a/src/domain/service/user.ts b/src/domain/service/user.ts index f734bdb3..c58d3b93 100644 --- a/src/domain/service/user.ts +++ b/src/domain/service/user.ts @@ -47,15 +47,18 @@ export default class UserService { } /** - * Get user extensions that contains only editoTools for now - * TODO: Simplify extenisons + * Get user editor tools ids * * @param userId - user unique identifier */ - public async getUserExtensions(userId: User['id']): Promise { + public async getUserEditorTools(userId: User['id']): Promise { const user = await this.getUserById(userId); - return user?.extensions ?? {}; + if (user === null) { + throw new Error('User not found'); + } + + return user.editorTools ?? []; } /** @@ -65,16 +68,14 @@ export default class UserService { */ public async addUserEditorTool({ userId, - editorToolId, + toolId, }: { userId: User['id'], - editorToolId: EditorTool['id'], + toolId: EditorTool['id'], }): Promise { return await this.repository.addUserEditorTool({ userId, - tool: { - id: editorToolId, - }, + toolId, }); } } diff --git a/src/presentation/http/router/user.ts b/src/presentation/http/router/user.ts index 30199607..f2aa2de8 100644 --- a/src/presentation/http/router/user.ts +++ b/src/presentation/http/router/user.ts @@ -63,7 +63,7 @@ const UserRouter: FastifyPluginCallback = (fastify, opts, don }); /** - * Get user extensions + * Get user editor tools */ fastify.get('/editor-tools', { config: { @@ -93,9 +93,7 @@ const UserRouter: FastifyPluginCallback = (fastify, opts, don }, async (request, reply) => { const userId = request.userId as number; - const userExtensions = await userService.getUserExtensions(userId); - const userEditorToolIds = userExtensions?.editorTools?.map(tools => tools.id) ?? []; - + const userEditorToolIds = await userService.getUserEditorTools(userId); const defaultEditorTools = await editorToolsService.getDefaultEditorTools(); const uniqueDefaultEditorTools = defaultEditorTools.filter(({ id }) => !userEditorToolIds.includes(id)); const userEditorTools = await editorToolsService.getToolsByIds(userEditorToolIds) ?? []; @@ -131,16 +129,16 @@ const UserRouter: FastifyPluginCallback = (fastify, opts, don }, }, }, async (request, reply) => { - const editorToolId = request.body.toolId; + const toolId = request.body.toolId; const userId = request.userId as number; await userService.addUserEditorTool({ userId, - editorToolId, + toolId, }); return reply.send({ - data: editorToolId, + data: toolId, }); }); diff --git a/src/presentation/http/schema/User.ts b/src/presentation/http/schema/User.ts index 9061d2f3..4f03ad48 100644 --- a/src/presentation/http/schema/User.ts +++ b/src/presentation/http/schema/User.ts @@ -10,4 +10,11 @@ export const UserSchema = { name: { type: 'string' }, photo: { type: 'string' }, }, + editorTools: { + type: 'array', + description: 'List of editor tools ids installed by user from Marketplace', + items: { + type: 'string', + }, + }, }; diff --git a/src/repository/storage/postgres/orm/sequelize/editorTools.ts b/src/repository/storage/postgres/orm/sequelize/editorTools.ts index 3daad4fa..7dbb62dc 100644 --- a/src/repository/storage/postgres/orm/sequelize/editorTools.ts +++ b/src/repository/storage/postgres/orm/sequelize/editorTools.ts @@ -13,7 +13,7 @@ export class EditorToolModel extends Model, Inf public declare id: EditorTool['id']; /** - * Custom name that uses in editor initiazliation. e.g. 'code' + * Custom name that uses in editor initialization. e.g. 'code' */ public declare name: EditorTool['name']; @@ -112,7 +112,7 @@ export default class UserSequelizeStorage { source, isDefault, }: EditorTool): Promise { - const editorTool = await this.model.create({ + return await this.model.create({ id, name, title, @@ -120,8 +120,6 @@ export default class UserSequelizeStorage { source, isDefault, }); - - return editorTool; } /** @@ -130,36 +128,30 @@ export default class UserSequelizeStorage { * @param editorToolIds - tool ids */ public async getToolsByIds(editorToolIds: EditorTool['id'][]): Promise { - const editorTools = await this.model.findAll({ + return await this.model.findAll({ where: { id: { [Op.in]: editorToolIds, }, }, }); - - return editorTools; } /** * Get all default tools */ public async getDefaultEditorTools(): Promise { - const editorTools = await this.model.findAll({ + return await this.model.findAll({ where: { isDefault: true, }, }); - - return editorTools; } /** * Get all available editor tools */ public async getTools(): Promise { - const editorTools = await EditorToolModel.findAll(); - - return editorTools; + return await EditorToolModel.findAll(); } } diff --git a/src/repository/storage/postgres/orm/sequelize/index.ts b/src/repository/storage/postgres/orm/sequelize/index.ts index 93e3dc10..f8a2c9eb 100644 --- a/src/repository/storage/postgres/orm/sequelize/index.ts +++ b/src/repository/storage/postgres/orm/sequelize/index.ts @@ -28,6 +28,12 @@ export default class SequelizeOrm { this.conn = new Sequelize(this.config.dsn, { logging: databaseLogger.info.bind(databaseLogger), + define: { + /** + * Use snake_case for fields in db, but camelCase in code + */ + underscored: true, + }, }); } diff --git a/src/repository/storage/postgres/orm/sequelize/note.ts b/src/repository/storage/postgres/orm/sequelize/note.ts index 42e24449..bb03092d 100644 --- a/src/repository/storage/postgres/orm/sequelize/note.ts +++ b/src/repository/storage/postgres/orm/sequelize/note.ts @@ -103,7 +103,7 @@ export default class NoteSequelizeStorage { }, { tableName: this.tableName, sequelize: this.database, - underscored: true, // use snake_case for fields in db + underscored: true, }); } @@ -131,13 +131,11 @@ export default class NoteSequelizeStorage { * @returns { Note } - created note */ public async createNote(options: NoteCreationAttributes): Promise { - const createdNote = await this.model.create({ + return await this.model.create({ publicId: options.publicId, content: options.content, creatorId: options.creatorId, }); - - return createdNote; } /** @@ -170,20 +168,11 @@ export default class NoteSequelizeStorage { * @param id - internal id */ public async getNoteById(id: NoteInternalId): Promise { - const note = await this.model.findOne({ + return await this.model.findOne({ where: { id, }, }); - - /** - * If note not found, return null - */ - if (!note) { - return null; - } - - return note; } /** @@ -213,15 +202,13 @@ export default class NoteSequelizeStorage { * @returns { Promise } note */ public async getNoteListByCreatorId(creatorId: number, offset: number, limit: number): Promise { - const noteList = await this.model.findAll({ + return await this.model.findAll({ offset: offset, limit: limit, where: { creatorId, }, }); - - return noteList; } /** * Gets note by id @@ -256,12 +243,10 @@ export default class NoteSequelizeStorage { * @returns { Promise } found note */ public async getNoteByPublicId(publicId: NotePublicId): Promise { - const note = await this.model.findOne({ + return await this.model.findOne({ where: { publicId, }, }); - - return note; }; } diff --git a/src/repository/storage/postgres/orm/sequelize/noteSettings.ts b/src/repository/storage/postgres/orm/sequelize/noteSettings.ts index 04c8af68..2c36ba01 100644 --- a/src/repository/storage/postgres/orm/sequelize/noteSettings.ts +++ b/src/repository/storage/postgres/orm/sequelize/noteSettings.ts @@ -92,7 +92,6 @@ export default class NoteSettingsSequelizeStorage { tableName: this.tableName, sequelize: this.database, timestamps: false, - underscored: true, // use snake_case for fields in db }); } diff --git a/src/repository/storage/postgres/orm/sequelize/user.ts b/src/repository/storage/postgres/orm/sequelize/user.ts index 242137c8..53585523 100644 --- a/src/repository/storage/postgres/orm/sequelize/user.ts +++ b/src/repository/storage/postgres/orm/sequelize/user.ts @@ -3,7 +3,7 @@ import { fn, col } from 'sequelize'; import { Model, DataTypes } from 'sequelize'; import type Orm from '@repository/storage/postgres/orm/sequelize/index.js'; import type User from '@domain/entities/user.js'; -import type { UserEditorTool } from '@domain/entities/userExtensions.js'; +import type EditorTool from '@domain/entities/editorTools'; /** * Query options for getting user @@ -47,9 +47,9 @@ export interface AddUserToolOptions { userId: User['id']; /** - * Editor tool data + * Editor tool identifier */ - tool: UserEditorTool; + toolId: EditorTool['id']; } /** @@ -62,9 +62,9 @@ interface RemoveUserEditorTool { userId: User['id']; /** - * Editor tool + * Editor tool identifier */ - editorTool: UserEditorTool; + toolId: EditorTool['id']; } /* eslint-disable @typescript-eslint/naming-convention */ @@ -91,7 +91,7 @@ export class UserModel extends Model, InferCreationAt /** * User created at */ - public declare created_at: Date; + public declare createdAt: Date; /** * User photo @@ -99,9 +99,9 @@ export class UserModel extends Model, InferCreationAt public declare photo: CreationOptional; /** - * + * List of tools ids installed by user from Marketplace */ - public declare extensions: CreationOptional; + public declare editorTools: CreationOptional; } /** @@ -149,15 +149,15 @@ export default class UserSequelizeStorage { type: DataTypes.STRING, allowNull: false, }, - created_at: { + createdAt: { type: DataTypes.DATE, allowNull: false, }, photo: { type: DataTypes.STRING, }, - extensions: { - type: DataTypes.JSON, + editorTools: { + type: DataTypes.ARRAY(DataTypes.STRING), }, }, { tableName: this.tableName, @@ -173,14 +173,14 @@ export default class UserSequelizeStorage { */ public async addUserEditorTool({ userId, - tool: editorTool, + toolId, }: AddUserToolOptions): Promise { await this.model.update({ - extensions: fn('array_append', col('editorTools'), editorTool), + editorTools: fn('array_append', col('editor_tools'), toolId), }, { where: { id: userId, - // TODO: Add check to unique editorTool id + // @todo Add check to unique editorTool id }, }); } @@ -192,10 +192,10 @@ export default class UserSequelizeStorage { */ public async removeUserEditorTool({ userId, - editorTool, + toolId, }: RemoveUserEditorTool): Promise { await this.model.update({ - extensions: fn('array_remove', col('editorTools'), editorTool), + editorTools: fn('array_remove', col('editor_tools'), toolId), }, { where: { id: userId, @@ -214,20 +214,12 @@ export default class UserSequelizeStorage { name, photo, }: InsertUserOptions): Promise { - const user = await this.model.create({ + return await this.model.create({ email, name, - created_at: new Date(), + createdAt: new Date(), photo, }); - - return { - id: user.id, - email: user.email, - name: user.name, - createdAt: user.created_at, - photo: user.photo, - }; } /** @@ -274,13 +266,6 @@ export default class UserSequelizeStorage { return null; } - return { - id: user.id, - email: user.email, - name: user.name, - createdAt: user.created_at, - photo: user.photo, - extensions: user.extensions, - }; + return user; } } diff --git a/src/repository/storage/postgres/orm/sequelize/userSession.ts b/src/repository/storage/postgres/orm/sequelize/userSession.ts index 317e1b07..e602b006 100644 --- a/src/repository/storage/postgres/orm/sequelize/userSession.ts +++ b/src/repository/storage/postgres/orm/sequelize/userSession.ts @@ -18,17 +18,17 @@ class UserSessionModel extends Model, InferCre /** * User id */ - public declare user_id: number; + public declare userId: number; /** * Refresh token */ - public declare refresh_token: string; + public declare refreshToken: string; /** * Refresh token expiration date */ - public declare refresh_token_expires_at: Date; + public declare refreshTokenExpiresAt: Date; } @@ -68,7 +68,7 @@ export default class UserSessionSequelizeStorage { autoIncrement: true, primaryKey: true, }, - user_id: { + userId: { type: DataTypes.INTEGER, allowNull: false, references: { @@ -76,12 +76,12 @@ export default class UserSessionSequelizeStorage { key: 'id', }, }, - refresh_token: { + refreshToken: { type: DataTypes.STRING, allowNull: false, unique: true, }, - refresh_token_expires_at: { + refreshTokenExpiresAt: { type: DataTypes.DATE, allowNull: false, }, @@ -101,18 +101,11 @@ export default class UserSessionSequelizeStorage { * @returns { UserSession } created user session */ public async create(userId: number, refreshToken: string, refreshTokenExpiresAt: Date): Promise { - const session = await this.model.create({ - user_id: userId, - refresh_token: refreshToken, - refresh_token_expires_at: refreshTokenExpiresAt, + return await this.model.create({ + userId: userId, + refreshToken: refreshToken, + refreshTokenExpiresAt: refreshTokenExpiresAt, }); - - return { - id: session.id, - userId: session.user_id, - refreshToken: session.refresh_token, - refreshTokenExpiresAt: session.refresh_token_expires_at, - }; } /** @@ -122,20 +115,9 @@ export default class UserSessionSequelizeStorage { * @returns { UserSession | null } found user session */ public async findByToken(token: string): Promise { - const session = await this.model.findOne({ - where: { refresh_token: token }, + return await this.model.findOne({ + where: { refreshToken: token }, }); - - if (!session) { - return null; - } - - return { - id: session.id, - userId: session.user_id, - refreshToken: session.refresh_token, - refreshTokenExpiresAt: session.refresh_token_expires_at, - }; } /** @@ -146,7 +128,9 @@ export default class UserSessionSequelizeStorage { */ public async removeByRefreshToken(refreshToken: string): Promise { await this.model.destroy({ - where: { refresh_token: refreshToken }, + where: { + refreshToken, + }, }); } } diff --git a/src/repository/user.repository.ts b/src/repository/user.repository.ts index 0705c2ef..f3018149 100644 --- a/src/repository/user.repository.ts +++ b/src/repository/user.repository.ts @@ -1,9 +1,9 @@ import type User from '@domain/entities/user.js'; -import type { UserEditorTool } from '@domain/entities/userExtensions.js'; import type UserStorage from '@repository/storage/user.storage.js'; import type GoogleApiTransport from '@repository/transport/google-api/index.js'; import type GetUserInfoResponsePayload from '@repository/transport/google-api/types/GetUserInfoResponsePayload.js'; import type { AddUserToolOptions } from '@repository/storage/postgres/orm/sequelize/user'; +import type EditorTool from '@domain/entities/editorTools'; /** * OAuth provider @@ -27,7 +27,7 @@ interface RemoveUserEditorToolOptions { /** * Editor tool identifier */ - editorToolId: UserEditorTool['id']; + toolId: EditorTool['id']; } /** @@ -127,10 +127,10 @@ export default class UserRepository { * * @param options - identifiers of user and tool */ - public async addUserEditorTool({ userId, tool }: AddUserToolOptions): Promise { + public async addUserEditorTool({ userId, toolId }: AddUserToolOptions): Promise { await this.storage.addUserEditorTool({ userId, - tool, + toolId, }); } @@ -139,22 +139,22 @@ export default class UserRepository { * * @param options - identifiers of user and tool */ - public async removeUserEditorTool({ userId, editorToolId }: RemoveUserEditorToolOptions): Promise { + public async removeUserEditorTool({ userId, toolId }: RemoveUserEditorToolOptions): Promise { const user = await this.getUserById(userId); if (!user) { - throw new Error('There is no user with such userId'); + throw new Error('There is no user with such id'); } - const editorTool = user.extensions?.editorTools?.find(tool => tool.id === editorToolId); + const editorTool = user.editorTools?.find(id => id === toolId); - if (!editorTool) { - throw new Error('User has no tool with such editorToolId'); + if (editorTool === undefined) { + throw new Error('User has no tool with such id'); } await this.storage.removeUserEditorTool({ userId, - editorTool, + toolId: editorTool, }); } } From bd21b03a28ce319f2efb9075f328b10cd94b46ca Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 23 Oct 2023 00:18:27 +0300 Subject: [PATCH 03/40] fix: migrations --- ...r-tools@rename-camelcase-to-underscore.sql | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql b/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql index 7ee601fd..46c2de25 100644 --- a/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql +++ b/migrations/tenant/0009-editor-tools@rename-camelcase-to-underscore.sql @@ -1,24 +1,33 @@ -/* - Rename columns in "editor_tools" table: -*/ --- Rename column "isDefault" to "is_default" if exists +-- "editor_tools" table: + +-- Rename column "isDefault" to "is_default" if "isDefault" exists and "is_default" not exists DO $$ BEGIN IF EXISTS(SELECT * FROM information_schema.columns WHERE table_name='editor_tools' and column_name='isDefault') THEN - ALTER TABLE "public"."editor_tools" RENAME COLUMN "isDefault" TO "is_default"; + IF NOT EXISTS(SELECT * + FROM information_schema.columns + WHERE table_name='editor_tools' and column_name='is_default') + THEN + ALTER TABLE "public"."editor_tools" RENAME COLUMN "isDefault" TO "is_default"; + END IF; END IF; END $$; --- Rename column "exportName" to "export_name" if exists +-- Rename column "exportName" to "export_name" if "exportName" exists and "export_name" not exists DO $$ BEGIN IF EXISTS(SELECT * FROM information_schema.columns WHERE table_name='editor_tools' and column_name='exportName') THEN - ALTER TABLE "public"."editor_tools" RENAME COLUMN "exportName" TO "export_name"; + IF NOT EXISTS(SELECT * + FROM information_schema.columns + WHERE table_name='editor_tools' and column_name='export_name') + THEN + ALTER TABLE "public"."editor_tools" RENAME COLUMN "exportName" TO "export_name"; + END IF; END IF; END $$; From 648fee11599830494e7eda5095c61c7aae503dba Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 23 Oct 2023 00:21:19 +0300 Subject: [PATCH 04/40] rm unused option --- src/repository/storage/postgres/orm/sequelize/note.ts | 1 - src/repository/storage/postgres/orm/sequelize/teams.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/repository/storage/postgres/orm/sequelize/note.ts b/src/repository/storage/postgres/orm/sequelize/note.ts index bb03092d..03f40a3e 100644 --- a/src/repository/storage/postgres/orm/sequelize/note.ts +++ b/src/repository/storage/postgres/orm/sequelize/note.ts @@ -103,7 +103,6 @@ export default class NoteSequelizeStorage { }, { tableName: this.tableName, sequelize: this.database, - underscored: true, }); } diff --git a/src/repository/storage/postgres/orm/sequelize/teams.ts b/src/repository/storage/postgres/orm/sequelize/teams.ts index ee241656..d6df080a 100644 --- a/src/repository/storage/postgres/orm/sequelize/teams.ts +++ b/src/repository/storage/postgres/orm/sequelize/teams.ts @@ -104,7 +104,6 @@ export default class TeamsSequelizeStorage { tableName: this.tableName, sequelize: this.database, timestamps: false, - underscored: true, // use snake_case for fields in db }); } From ef2e923c5365f699a7f1e3c249b6161d84e91ab3 Mon Sep 17 00:00:00 2001 From: e11sy Date: Mon, 23 Oct 2023 01:48:46 +0300 Subject: [PATCH 05/40] feat(noteList.test) *added test added for /notes of one user *added insertUserSessions function *added json file for insertUserSessions function *added notes to notes.json (now 50) with one user_id *renamed notes-settings to camelcase notesSettings now --- src/presentation/http/router/noteList.test.ts | 97 +++++ src/tests/test-data/notes.json | 394 +++++++++++++++++- ...notes-settings.json => notesSettings.json} | 0 src/tests/test-data/userSessions.json | 8 + src/tests/utils/insert-data.ts | 14 +- 5 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 src/presentation/http/router/noteList.test.ts rename src/tests/test-data/{notes-settings.json => notesSettings.json} (100%) create mode 100644 src/tests/test-data/userSessions.json diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts new file mode 100644 index 00000000..79a19afb --- /dev/null +++ b/src/presentation/http/router/noteList.test.ts @@ -0,0 +1,97 @@ +import { describe, test, expect } from 'vitest'; +import userSessions from 'tests/test-data/userSessions.json'; + +const refresh_token = userSessions[0]['refresh_tocken']; +const access_token = await global.api?.fakeRequest({ + method: 'POST', + url: `/auth/?token=${refresh_token}`, +}); + +describe('NoteList API', () => { + describe('GET notes?page', () => { + test('Returns noteList with specified lenght (not for last page)', async () => { + const expectedStatus = 200; + const portionSize = 30; + + const response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${access_token}`, + }, + url: '/notes/&page=1', + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toHaveLength(portionSize); + }); + + test('Returns noteList with specified lenght (for last page)', async () => { + const expectedStatus = 200; + const portionSize = 20; + + const response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${access_token}`, + }, + url: '/notes/&page=2', + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toHaveLength(portionSize); + }); + + test('Returns noteList with no items if page*portionSize > numberOfNotes', async () => { + const expectedStatus = 200; + + const response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${access_token}`, + }, + url: '/notes/&page=3', + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toEqual([]); + expect(body).toHaveLength(0); + }); + + test('Returns 400 when page < 0', async () => { + const expextedStatus = 400; + + const response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${access_token}`, + }, + url: '/notes/&page=0', + }); + + expect(response?.statusCode).toBe(expextedStatus); + }); + + test('Returns 400 when page > 0', async () => { + const expextedStatus = 400; + + const response = await global.api?.fakeRequest({ + method: 'GET', + headers: { + authorization: `Bearer ${access_token}`, + }, + url: '/notes/&page=31', + }); + + expect(response?.statusCode).toBe(expextedStatus); + }); + }); +}); \ No newline at end of file diff --git a/src/tests/test-data/notes.json b/src/tests/test-data/notes.json index 4fc09cbf..32a5c1c0 100644 --- a/src/tests/test-data/notes.json +++ b/src/tests/test-data/notes.json @@ -6,5 +6,397 @@ "content": [], "created_at": "2023-10-16T13:49:19.000Z", "updated_at": "2023-10-16T13:49:19.000Z" + }, + { + "id": 2, + "public_id": "99999902", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:2.000Z", + "updated_at": "2023-10-16T13:49:2.000Z" + }, + { + "id": 3, + "public_id": "99999903", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:3.000Z", + "updated_at": "2023-10-16T13:49:3.000Z" + }, + { + "id": 4, + "public_id": "99999904", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:4.000Z", + "updated_at": "2023-10-16T13:49:4.000Z" + }, + { + "id": 5, + "public_id": "99999905", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:5.000Z", + "updated_at": "2023-10-16T13:49:5.000Z" + }, + { + "id": 6, + "public_id": "99999906", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:6.000Z", + "updated_at": "2023-10-16T13:49:6.000Z" + }, + { + "id": 7, + "public_id": "99999907", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:7.000Z", + "updated_at": "2023-10-16T13:49:7.000Z" + }, + { + "id": 8, + "public_id": "99999908", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:8.000Z", + "updated_at": "2023-10-16T13:49:8.000Z" + }, + { + "id": 9, + "public_id": "99999909", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:9.000Z", + "updated_at": "2023-10-16T13:49:9.000Z" + }, + { + "id": 10, + "public_id": "99999910", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:10.000Z", + "updated_at": "2023-10-16T13:49:10.000Z" + }, + { + "id": 11, + "public_id": "99999911", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:11.000Z", + "updated_at": "2023-10-16T13:49:11.000Z" + }, + { + "id": 12, + "public_id": "99999912", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:12.000Z", + "updated_at": "2023-10-16T13:49:12.000Z" + }, + { + "id": 13, + "public_id": "99999913", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:13.000Z", + "updated_at": "2023-10-16T13:49:13.000Z" + }, + { + "id": 14, + "public_id": "99999914", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:14.000Z", + "updated_at": "2023-10-16T13:49:14.000Z" + }, + { + "id": 15, + "public_id": "99999915", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:15.000Z", + "updated_at": "2023-10-16T13:49:15.000Z" + }, + { + "id": 16, + "public_id": "99999916", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:16.000Z", + "updated_at": "2023-10-16T13:49:16.000Z" + }, + { + "id": 17, + "public_id": "99999917", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:17.000Z", + "updated_at": "2023-10-16T13:49:17.000Z" + }, + { + "id": 18, + "public_id": "99999918", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:18.000Z", + "updated_at": "2023-10-16T13:49:18.000Z" + }, + { + "id": 19, + "public_id": "99999919", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:19.000Z", + "updated_at": "2023-10-16T13:49:19.000Z" + }, + { + "id": 20, + "public_id": "99999920", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:20.000Z", + "updated_at": "2023-10-16T13:49:20.000Z" + }, + { + "id": 21, + "public_id": "99999921", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:21.000Z", + "updated_at": "2023-10-16T13:49:21.000Z" + }, + { + "id": 22, + "public_id": "99999922", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:22.000Z", + "updated_at": "2023-10-16T13:49:22.000Z" + }, + { + "id": 23, + "public_id": "99999923", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:23.000Z", + "updated_at": "2023-10-16T13:49:23.000Z" + }, + { + "id": 24, + "public_id": "99999924", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:24.000Z", + "updated_at": "2023-10-16T13:49:24.000Z" + }, + { + "id": 25, + "public_id": "99999925", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:25.000Z", + "updated_at": "2023-10-16T13:49:25.000Z" + }, + { + "id": 26, + "public_id": "99999926", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:26.000Z", + "updated_at": "2023-10-16T13:49:26.000Z" + }, + { + "id": 27, + "public_id": "99999927", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:27.000Z", + "updated_at": "2023-10-16T13:49:27.000Z" + }, + { + "id": 28, + "public_id": "99999928", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:28.000Z", + "updated_at": "2023-10-16T13:49:28.000Z" + }, + { + "id": 29, + "public_id": "99999929", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:29.000Z", + "updated_at": "2023-10-16T13:49:29.000Z" + }, + { + "id": 30, + "public_id": "99999930", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:30.000Z", + "updated_at": "2023-10-16T13:49:30.000Z" + }, + { + "id": 31, + "public_id": "99999931", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:31.000Z", + "updated_at": "2023-10-16T13:49:31.000Z" + }, + { + "id": 32, + "public_id": "99999932", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:32.000Z", + "updated_at": "2023-10-16T13:49:32.000Z" + }, + { + "id": 33, + "public_id": "99999933", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:33.000Z", + "updated_at": "2023-10-16T13:49:33.000Z" + }, + { + "id": 34, + "public_id": "99999934", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:34.000Z", + "updated_at": "2023-10-16T13:49:34.000Z" + }, + { + "id": 35, + "public_id": "99999935", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:35.000Z", + "updated_at": "2023-10-16T13:49:35.000Z" + }, + { + "id": 36, + "public_id": "99999936", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:36.000Z", + "updated_at": "2023-10-16T13:49:36.000Z" + }, + { + "id": 37, + "public_id": "99999937", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:37.000Z", + "updated_at": "2023-10-16T13:49:37.000Z" + }, + { + "id": 38, + "public_id": "99999938", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:38.000Z", + "updated_at": "2023-10-16T13:49:38.000Z" + }, + { + "id": 39, + "public_id": "99999939", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:39.000Z", + "updated_at": "2023-10-16T13:49:39.000Z" + }, + { + "id": 40, + "public_id": "99999940", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:40.000Z", + "updated_at": "2023-10-16T13:49:40.000Z" + }, + { + "id": 41, + "public_id": "99999941", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:41.000Z", + "updated_at": "2023-10-16T13:49:41.000Z" + }, + { + "id": 42, + "public_id": "99999942", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:42.000Z", + "updated_at": "2023-10-16T13:49:42.000Z" + }, + { + "id": 43, + "public_id": "99999943", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:43.000Z", + "updated_at": "2023-10-16T13:49:43.000Z" + }, + { + "id": 44, + "public_id": "99999944", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:44.000Z", + "updated_at": "2023-10-16T13:49:44.000Z" + }, + { + "id": 45, + "public_id": "99999945", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:45.000Z", + "updated_at": "2023-10-16T13:49:45.000Z" + }, + { + "id": 46, + "public_id": "99999946", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:46.000Z", + "updated_at": "2023-10-16T13:49:46.000Z" + }, + { + "id": 47, + "public_id": "99999947", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:47.000Z", + "updated_at": "2023-10-16T13:49:47.000Z" + }, + { + "id": 48, + "public_id": "99999948", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:48.000Z", + "updated_at": "2023-10-16T13:49:48.000Z" + }, + { + "id": 49, + "public_id": "99999949", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:49.000Z", + "updated_at": "2023-10-16T13:49:49.000Z" + }, + { + "id": 50, + "public_id": "99999950", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:50.000Z", + "updated_at": "2023-10-16T13:49:50.000Z" } -] +] \ No newline at end of file diff --git a/src/tests/test-data/notes-settings.json b/src/tests/test-data/notesSettings.json similarity index 100% rename from src/tests/test-data/notes-settings.json rename to src/tests/test-data/notesSettings.json diff --git a/src/tests/test-data/userSessions.json b/src/tests/test-data/userSessions.json new file mode 100644 index 00000000..892333ca --- /dev/null +++ b/src/tests/test-data/userSessions.json @@ -0,0 +1,8 @@ +[ + { + "id" : 1, + "user_id" : 4, + "refresh_tocken" : "IqrTkSKmel", + "refresh_tocken_expites_at" : "2023-11-21 19:19:40.911+03" + } +] \ No newline at end of file diff --git a/src/tests/utils/insert-data.ts b/src/tests/utils/insert-data.ts index cbb3c590..27dd56ef 100644 --- a/src/tests/utils/insert-data.ts +++ b/src/tests/utils/insert-data.ts @@ -1,7 +1,8 @@ import type SequelizeOrm from '@repository/storage/postgres/orm/index.js'; import users from '../test-data/users.json'; +import userSessions from '../test-data/userSessions.json'; import notes from '../test-data/notes.json'; -import noteSettings from '../test-data/notes-settings.json'; +import noteSettings from '../test-data/notesSettings.json'; /** * Fills in the database with users data @@ -14,6 +15,16 @@ async function insertUsers(db: SequelizeOrm): Promise { } } +/** + * Fills in the database with user sessions + * + * @param db - SequelizeOrm instance + */ +async function insertUserSessions(db: SequelizeOrm): Promise { + for (const userSession of userSessions) { + await db.connection.query(`INSERT INTO public.userSessions (id, user_id, refresh_token, refresh_token_expires_at) VALUES (${userSession.id}, ${userSession.user_id}, ${userSession.refresh_tocken}, ${userSession.refresh_tocken_expites_at})`); + } +} /** * Fills in the database with notes data * @@ -44,6 +55,7 @@ async function insertNoteSettings(db: SequelizeOrm): Promise { */ export async function insertData(db: SequelizeOrm): Promise { await insertUsers(db); + await insertUserSessions(db); await insertNotes(db); await insertNoteSettings(db); } From 7c3d21260e7ec75ad531627f5e065612351998e2 Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 23 Oct 2023 02:48:12 +0300 Subject: [PATCH 06/40] chore: move bis-logic from ui do domain, decouple domains --- src/domain/index.ts | 10 +++++++--- src/domain/service/editorTools.ts | 9 +++++---- src/domain/service/shared/README.md | 6 ++++++ src/domain/service/shared/editorTools.ts | 19 +++++++++++++++++++ src/domain/service/shared/index.ts | 5 +++++ src/domain/service/user.ts | 18 +++++++++++++++--- src/presentation/http/router/user.ts | 12 ++---------- src/repository/editorTools.repository.ts | 4 ++-- src/repository/note.repository.ts | 2 -- .../postgres/orm/sequelize/editorTools.ts | 2 +- 10 files changed, 62 insertions(+), 25 deletions(-) create mode 100644 src/domain/service/shared/README.md create mode 100644 src/domain/service/shared/editorTools.ts create mode 100644 src/domain/service/shared/index.ts diff --git a/src/domain/index.ts b/src/domain/index.ts index 8e88151c..c061e04e 100644 --- a/src/domain/index.ts +++ b/src/domain/index.ts @@ -63,10 +63,14 @@ export function init(repositories: Repositories, appConfig: AppConfig): DomainSe repositories.userSessionRepository ); - const userService = new UserService(repositories.userRepository); - - const aiService = new AIService(repositories.aiRepository); const editorToolsService = new EditorToolsService(repositories.editorToolsRepository); + const userService = new UserService(repositories.userRepository, { + editorTools: editorToolsService, + /** + * @todo find a way how to resolve circular dependency + */ + }); + const aiService = new AIService(repositories.aiRepository); return { noteService, diff --git a/src/domain/service/editorTools.ts b/src/domain/service/editorTools.ts index c956e0f8..c0a14282 100644 --- a/src/domain/service/editorTools.ts +++ b/src/domain/service/editorTools.ts @@ -1,11 +1,12 @@ import type EditorToolsRepository from '@repository/editorTools.repository.js'; import type EditorTool from '@domain/entities/editorTools.js'; import { createEditorToolId } from '@infrastructure/utils/id.js'; +import type EditorToolsServiceSharedMethods from './shared/editorTools.js'; /** * Editor tools service */ -export default class EditorToolsService { +export default class EditorToolsService implements EditorToolsServiceSharedMethods { /** * User repository instance */ @@ -37,10 +38,10 @@ export default class EditorToolsService { } /** - * Get all default tools + * Return tools that are available at Editor by default */ - public async getDefaultEditorTools(): Promise { - return await this.repository.getDefaultEditorTools(); + public async getDefaultTools(): Promise { + return await this.repository.getDefaultTools(); } /** diff --git a/src/domain/service/shared/README.md b/src/domain/service/shared/README.md new file mode 100644 index 00000000..92c8f628 --- /dev/null +++ b/src/domain/service/shared/README.md @@ -0,0 +1,6 @@ +# Shared Domain Services + +Sometimes you may want to call some domain method from other domain. We can inject them in constructor, but it creates a direct dependency. + +One way do decouple domains is to create a Shared Interfaces — domain will "expose" some public methods though it. You can call it "Contract". +So dependant domain will depend on it instead of direct dependency. diff --git a/src/domain/service/shared/editorTools.ts b/src/domain/service/shared/editorTools.ts new file mode 100644 index 00000000..8f1d63a4 --- /dev/null +++ b/src/domain/service/shared/editorTools.ts @@ -0,0 +1,19 @@ +import type EditorTool from '@domain/entities/editorTools'; + +/** + * Which methods of Domain can be used by other domains + * Uses to decouple domains from each other + */ +export default interface EditorToolsServiceSharedMethods { + /** + * Return tools that are available at Editor by default + */ + getDefaultTools(): Promise; + + /** + * Get bunch of editor tools by their ids + * + * @param ids - tool ids to resolve + */ + getToolsByIds(ids: EditorTool['id'][]): Promise; +} diff --git a/src/domain/service/shared/index.ts b/src/domain/service/shared/index.ts new file mode 100644 index 00000000..11364d8f --- /dev/null +++ b/src/domain/service/shared/index.ts @@ -0,0 +1,5 @@ +import type EditorToolsServiceSharedMethods from './editorTools'; + +export type SharedDomainMethods = { + editorTools: EditorToolsServiceSharedMethods; +}; diff --git a/src/domain/service/user.ts b/src/domain/service/user.ts index c58d3b93..b54a0ea1 100644 --- a/src/domain/service/user.ts +++ b/src/domain/service/user.ts @@ -2,6 +2,7 @@ import type UserRepository from '@repository/user.repository.js'; import { Provider } from '@repository/user.repository.js'; import type User from '@domain/entities/user.js'; import type EditorTool from '@domain/entities/editorTools'; +import type { SharedDomainMethods } from './shared/index.js'; export { Provider @@ -20,8 +21,9 @@ export default class UserService { * User service constructor * * @param repository - user repository instance + * @param shared - shared domains */ - constructor(repository: UserRepository) { + constructor(repository: UserRepository, private readonly shared: SharedDomainMethods) { this.repository = repository; } @@ -51,14 +53,24 @@ export default class UserService { * * @param userId - user unique identifier */ - public async getUserEditorTools(userId: User['id']): Promise { + public async getUserEditorTools(userId: User['id']): Promise { const user = await this.getUserById(userId); if (user === null) { throw new Error('User not found'); } - return user.editorTools ?? []; + const userToolsIds = user.editorTools ?? []; + const defaultTools = await this.shared.editorTools.getDefaultTools(); + const uniqueDefaultEditorTools = defaultTools.filter(({ id }) => !userToolsIds.includes(id)); + const userTools = await this.shared.editorTools.getToolsByIds(userToolsIds) ?? []; + + /** + * Combine user tools and default tools + * + * @todo load tools in notes service + */ + return [...userTools, ...uniqueDefaultEditorTools]; } /** diff --git a/src/presentation/http/router/user.ts b/src/presentation/http/router/user.ts index f2aa2de8..965f8076 100644 --- a/src/presentation/http/router/user.ts +++ b/src/presentation/http/router/user.ts @@ -30,7 +30,6 @@ const UserRouter: FastifyPluginCallback = (fastify, opts, don * Manage user data */ const userService = opts.userService; - const editorToolsService = opts.editorToolsService; /** * Get user by session @@ -93,17 +92,10 @@ const UserRouter: FastifyPluginCallback = (fastify, opts, don }, async (request, reply) => { const userId = request.userId as number; - const userEditorToolIds = await userService.getUserEditorTools(userId); - const defaultEditorTools = await editorToolsService.getDefaultEditorTools(); - const uniqueDefaultEditorTools = defaultEditorTools.filter(({ id }) => !userEditorToolIds.includes(id)); - const userEditorTools = await editorToolsService.getToolsByIds(userEditorToolIds) ?? []; - - // Combine user tools and default tools - // TODO: load tools in notes service - const mergedTools = [...userEditorTools, ...uniqueDefaultEditorTools]; + const tools = await userService.getUserEditorTools(userId); return reply.send({ - data: mergedTools, + data: tools, }); }); diff --git a/src/repository/editorTools.repository.ts b/src/repository/editorTools.repository.ts index bbe15e68..c29dbe12 100644 --- a/src/repository/editorTools.repository.ts +++ b/src/repository/editorTools.repository.ts @@ -37,8 +37,8 @@ export default class EditorToolsRepository { /** * Get all default tools */ - public async getDefaultEditorTools(): Promise { - return await this.storage.getDefaultEditorTools(); + public async getDefaultTools(): Promise { + return await this.storage.getDefaultTools(); } /** diff --git a/src/repository/note.repository.ts b/src/repository/note.repository.ts index e2baa164..73677924 100644 --- a/src/repository/note.repository.ts +++ b/src/repository/note.repository.ts @@ -1,6 +1,5 @@ import type { Note, NoteCreationAttributes, NoteInternalId, NotePublicId } from '@domain/entities/note.js'; import type NoteStorage from '@repository/storage/note.storage.js'; -import type { NoteList } from '@domain/entities/noteList.js'; /** * Repository allows accessing data from business-logic (domain) level @@ -85,7 +84,6 @@ export default class NoteRepository { * @param id - note creator id * @param offset - number of skipped notes * @param limit - number of notes to get - * @returns { Promise } note */ public async getNoteListByCreatorId(id: number, offset: number, limit: number): Promise { return await this.storage.getNoteListByCreatorId(id, offset, limit); diff --git a/src/repository/storage/postgres/orm/sequelize/editorTools.ts b/src/repository/storage/postgres/orm/sequelize/editorTools.ts index 7dbb62dc..a431bb5d 100644 --- a/src/repository/storage/postgres/orm/sequelize/editorTools.ts +++ b/src/repository/storage/postgres/orm/sequelize/editorTools.ts @@ -140,7 +140,7 @@ export default class UserSequelizeStorage { /** * Get all default tools */ - public async getDefaultEditorTools(): Promise { + public async getDefaultTools(): Promise { return await this.model.findAll({ where: { isDefault: true, From 4e71ad0608fa1975de5dd82136bd5afb39d9dfdd Mon Sep 17 00:00:00 2001 From: e11sy Date: Mon, 23 Oct 2023 02:49:42 +0300 Subject: [PATCH 07/40] comment fixes - fixed comments logic - trying to fix directoru error --- src/presentation/http/router/noteList.test.ts | 24 ++++++++++++------- tsconfig.json | 5 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index 79a19afb..8dc6752a 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -1,5 +1,5 @@ +import userSessions from '@test/test-data/userSessions.json'; import { describe, test, expect } from 'vitest'; -import userSessions from 'tests/test-data/userSessions.json'; const refresh_token = userSessions[0]['refresh_tocken']; const access_token = await global.api?.fakeRequest({ @@ -8,17 +8,18 @@ const access_token = await global.api?.fakeRequest({ }); describe('NoteList API', () => { - describe('GET notes?page', () => { + describe('GET /notes?page', () => { test('Returns noteList with specified lenght (not for last page)', async () => { const expectedStatus = 200; const portionSize = 30; + const pageNumber = 1; const response = await global.api?.fakeRequest({ method: 'GET', headers: { authorization: `Bearer ${access_token}`, }, - url: '/notes/&page=1', + url: `/notes/&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -31,13 +32,14 @@ describe('NoteList API', () => { test('Returns noteList with specified lenght (for last page)', async () => { const expectedStatus = 200; const portionSize = 20; + const pageNumber = 2; const response = await global.api?.fakeRequest({ method: 'GET', headers: { authorization: `Bearer ${access_token}`, }, - url: '/notes/&page=2', + url: `/notes/&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -47,15 +49,16 @@ describe('NoteList API', () => { expect(body).toHaveLength(portionSize); }); - test('Returns noteList with no items if page*portionSize > numberOfNotes', async () => { + test('Returns noteList with no items if it has no notes', async () => { const expectedStatus = 200; + const pageNumber = 3; const response = await global.api?.fakeRequest({ method: 'GET', headers: { authorization: `Bearer ${access_token}`, }, - url: '/notes/&page=3', + url: `/notes/&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -68,27 +71,30 @@ describe('NoteList API', () => { test('Returns 400 when page < 0', async () => { const expextedStatus = 400; + const pageNumber = 0; + const response = await global.api?.fakeRequest({ method: 'GET', headers: { authorization: `Bearer ${access_token}`, }, - url: '/notes/&page=0', + url: `/notes/&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expextedStatus); }); - test('Returns 400 when page > 0', async () => { + test('Returns 400 when page is too large', async () => { const expextedStatus = 400; + const pageNumber = 31; const response = await global.api?.fakeRequest({ method: 'GET', headers: { authorization: `Bearer ${access_token}`, }, - url: '/notes/&page=31', + url: `/notes/&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expextedStatus); diff --git a/tsconfig.json b/tsconfig.json index 93e90d57..407fef84 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "outDir": "./dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "resolveJsonModule":true, + "resolveJsonModule": true, "strict": true, "skipLibCheck": true, "paths": { @@ -15,7 +15,8 @@ "@presentation/*": ["presentation/*"], "@lib/*": ["lib/*"], "@domain/*": ["domain/*"], - "@repository/*": ["repository/*"] + "@repository/*": ["repository/*"], + "@tests/*": ["tests/*"] }, }, "include": [ From 239ec7c3aca2fd7912027b199236cbbb4765ef15 Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 23 Oct 2023 02:54:42 +0300 Subject: [PATCH 08/40] Update README.md --- src/domain/service/shared/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/domain/service/shared/README.md b/src/domain/service/shared/README.md index 92c8f628..f49eb8a7 100644 --- a/src/domain/service/shared/README.md +++ b/src/domain/service/shared/README.md @@ -4,3 +4,27 @@ Sometimes you may want to call some domain method from other domain. We can inje One way do decouple domains is to create a Shared Interfaces — domain will "expose" some public methods though it. You can call it "Contract". So dependant domain will depend on it instead of direct dependency. + + +## Example + +```ts +interface DomainASharedMethods { + someMethodA: () => void; +} + +export type SharedDomainMethods = { + editorTools: DomainASharedMethods; +}; + + +class DomainA implements DomainASharedMethods { + public someMethodA (){} +} + +class DomainB { + constructor(private readonly shared: SharedDomainMethods) { + this.shared.someMethodA(); // here we call method of Domain A, but without direct dependency + } +} +``` From a4d6340c2549e36a2acbfec47cd94e46b3d7ebb7 Mon Sep 17 00:00:00 2001 From: GeekaN2 Date: Mon, 23 Oct 2023 03:03:54 +0300 Subject: [PATCH 09/40] chore: fix alias tests --- src/presentation/http/router/noteList.test.ts | 4 ++-- vitest.config.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index 8dc6752a..f7950ae9 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -1,4 +1,4 @@ -import userSessions from '@test/test-data/userSessions.json'; +import userSessions from '@tests/test-data/userSessions.json'; import { describe, test, expect } from 'vitest'; const refresh_token = userSessions[0]['refresh_tocken']; @@ -100,4 +100,4 @@ describe('NoteList API', () => { expect(response?.statusCode).toBe(expextedStatus); }); }); -}); \ No newline at end of file +}); diff --git a/vitest.config.js b/vitest.config.js index dcbc97f0..65930db4 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -14,6 +14,7 @@ export default defineConfig({ '@lib/': '/src/lib/', '@domain/': '/src/domain/', '@repository/': '/src/repository/', + '@tests/': '/src/tests/', }, }, }); From 3e7fc8fc78abfc6aaeff0a20a5052d2cebf865d6 Mon Sep 17 00:00:00 2001 From: e11sy Date: Mon, 23 Oct 2023 03:17:02 +0300 Subject: [PATCH 10/40] fixed sql syntax fixed sql syntax --- src/tests/utils/insert-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/utils/insert-data.ts b/src/tests/utils/insert-data.ts index 27dd56ef..8cc012bc 100644 --- a/src/tests/utils/insert-data.ts +++ b/src/tests/utils/insert-data.ts @@ -22,7 +22,7 @@ async function insertUsers(db: SequelizeOrm): Promise { */ async function insertUserSessions(db: SequelizeOrm): Promise { for (const userSession of userSessions) { - await db.connection.query(`INSERT INTO public.userSessions (id, user_id, refresh_token, refresh_token_expires_at) VALUES (${userSession.id}, ${userSession.user_id}, ${userSession.refresh_tocken}, ${userSession.refresh_tocken_expites_at})`); + await db.connection.query(`INSERT INTO public.user_sessions (id, user_id, refresh_token, refresh_token_expires_at) VALUES (${userSession.id}, ${userSession.user_id}, ${userSession.refresh_tocken}, ${userSession.refresh_tocken_expites_at})`); } } /** From c85ca2f333d3fdc8c86e24e6a1cc351c307097ba Mon Sep 17 00:00:00 2001 From: e11sy Date: Mon, 23 Oct 2023 03:20:27 +0300 Subject: [PATCH 11/40] fixed sql syntax fixed sql syntax --- src/tests/utils/insert-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/utils/insert-data.ts b/src/tests/utils/insert-data.ts index 8cc012bc..9a9e9799 100644 --- a/src/tests/utils/insert-data.ts +++ b/src/tests/utils/insert-data.ts @@ -22,7 +22,7 @@ async function insertUsers(db: SequelizeOrm): Promise { */ async function insertUserSessions(db: SequelizeOrm): Promise { for (const userSession of userSessions) { - await db.connection.query(`INSERT INTO public.user_sessions (id, user_id, refresh_token, refresh_token_expires_at) VALUES (${userSession.id}, ${userSession.user_id}, ${userSession.refresh_tocken}, ${userSession.refresh_tocken_expites_at})`); + await db.connection.query(`INSERT INTO public.user_sessions (id, "user_id", "refresh_token", "refresh_token_expires_at") VALUES (${userSession.id}, ${userSession.user_id}, '${userSession.refresh_tocken}', '${userSession.refresh_tocken_expites_at}')`); } } /** From ce0099e227622280c2e1398e17f8246b8379583b Mon Sep 17 00:00:00 2001 From: e11sy Date: Mon, 23 Oct 2023 03:23:20 +0300 Subject: [PATCH 12/40] changed variable name changed variable name --- src/presentation/http/router/noteList.test.ts | 2 +- src/tests/test-data/userSessions.json | 4 ++-- src/tests/utils/insert-data.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index f7950ae9..1853a696 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -1,7 +1,7 @@ import userSessions from '@tests/test-data/userSessions.json'; import { describe, test, expect } from 'vitest'; -const refresh_token = userSessions[0]['refresh_tocken']; +const refresh_token = userSessions[0]['refresh_token']; const access_token = await global.api?.fakeRequest({ method: 'POST', url: `/auth/?token=${refresh_token}`, diff --git a/src/tests/test-data/userSessions.json b/src/tests/test-data/userSessions.json index 892333ca..57693776 100644 --- a/src/tests/test-data/userSessions.json +++ b/src/tests/test-data/userSessions.json @@ -2,7 +2,7 @@ { "id" : 1, "user_id" : 4, - "refresh_tocken" : "IqrTkSKmel", - "refresh_tocken_expites_at" : "2023-11-21 19:19:40.911+03" + "refresh_token" : "IqrTkSKmel", + "refresh_token_expites_at" : "2023-11-21 19:19:40.911+03" } ] \ No newline at end of file diff --git a/src/tests/utils/insert-data.ts b/src/tests/utils/insert-data.ts index 9a9e9799..d9f134e8 100644 --- a/src/tests/utils/insert-data.ts +++ b/src/tests/utils/insert-data.ts @@ -22,7 +22,7 @@ async function insertUsers(db: SequelizeOrm): Promise { */ async function insertUserSessions(db: SequelizeOrm): Promise { for (const userSession of userSessions) { - await db.connection.query(`INSERT INTO public.user_sessions (id, "user_id", "refresh_token", "refresh_token_expires_at") VALUES (${userSession.id}, ${userSession.user_id}, '${userSession.refresh_tocken}', '${userSession.refresh_tocken_expites_at}')`); + await db.connection.query(`INSERT INTO public.user_sessions (id, "user_id", "refresh_token", "refresh_token_expires_at") VALUES (${userSession.id}, ${userSession.user_id}, '${userSession.refresh_token}', '${userSession.refresh_token_expites_at}')`); } } /** From fa36e2e9433042de8f28054ae8edc85a48f7880a Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Mon, 23 Oct 2023 12:11:42 +0300 Subject: [PATCH 13/40] fix readme --- src/domain/service/shared/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/service/shared/README.md b/src/domain/service/shared/README.md index f49eb8a7..b7d2d0f0 100644 --- a/src/domain/service/shared/README.md +++ b/src/domain/service/shared/README.md @@ -14,7 +14,7 @@ interface DomainASharedMethods { } export type SharedDomainMethods = { - editorTools: DomainASharedMethods; + domainA: DomainASharedMethods; }; @@ -24,7 +24,7 @@ class DomainA implements DomainASharedMethods { class DomainB { constructor(private readonly shared: SharedDomainMethods) { - this.shared.someMethodA(); // here we call method of Domain A, but without direct dependency + this.shared.domainA.someMethodA(); // here we call method of Domain A, but without direct dependency } } ``` From 63925eeaacd7f8f708519a2f3e9a6eeebca47d72 Mon Sep 17 00:00:00 2001 From: GoldenJaden Date: Tue, 24 Oct 2023 00:42:00 +0300 Subject: [PATCH 14/40] Divided enlist and build-push action to 2 separated actions --- .../workflows/build-and-push-docker-image.yml | 16 +------------ .github/workflows/run-eslint-check.yml | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/run-eslint-check.yml diff --git a/.github/workflows/build-and-push-docker-image.yml b/.github/workflows/build-and-push-docker-image.yml index 60042394..9e026232 100644 --- a/.github/workflows/build-and-push-docker-image.yml +++ b/.github/workflows/build-and-push-docker-image.yml @@ -11,25 +11,11 @@ env: REGISTRY: ghcr.io jobs: - eslint: - name: Run eslint check - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install dependencies - run: | - yarn install - - - name: Run ESLint - run: yarn lint - build: runs-on: ubuntu-22.04 steps: - - name: Checkout repository + - name: Checkout Repository uses: actions/checkout@v3 - name: Get full image name diff --git a/.github/workflows/run-eslint-check.yml b/.github/workflows/run-eslint-check.yml new file mode 100644 index 00000000..e08872b3 --- /dev/null +++ b/.github/workflows/run-eslint-check.yml @@ -0,0 +1,23 @@ +name: Run ESlint check + +on: + push: + branches: + - '*' + tags: + - 'v*' + +jobs: + eslint: + name: Run eslint check + runs-on: ubuntu-22.04 + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + yarn install + + - name: Run ESLint + run: yarn lint \ No newline at end of file From dfd1eaee8b48a65e9ef363e3757f6f9ba605ab21 Mon Sep 17 00:00:00 2001 From: GoldenJaden Date: Tue, 24 Oct 2023 22:15:18 +0300 Subject: [PATCH 15/40] Added build check workflow --- ...d-push-docker-image.yml => Deploy-API.yml} | 7 +++- .github/workflows/run-build-check.yml | 35 +++++++++++++++++++ .github/workflows/run-eslint-check.yml | 5 +++ 3 files changed, 46 insertions(+), 1 deletion(-) rename .github/workflows/{build-and-push-docker-image.yml => Deploy-API.yml} (94%) create mode 100644 .github/workflows/run-build-check.yml diff --git a/.github/workflows/build-and-push-docker-image.yml b/.github/workflows/Deploy-API.yml similarity index 94% rename from .github/workflows/build-and-push-docker-image.yml rename to .github/workflows/Deploy-API.yml index 9e026232..a9d20e51 100644 --- a/.github/workflows/build-and-push-docker-image.yml +++ b/.github/workflows/Deploy-API.yml @@ -1,4 +1,4 @@ -name: Build and push docker image +name: Deploy API on: push: @@ -6,6 +6,11 @@ on: - '*' tags: - 'v*' + + pull_request: + types: [opened, edited] + branches: + - '*' env: REGISTRY: ghcr.io diff --git a/.github/workflows/run-build-check.yml b/.github/workflows/run-build-check.yml new file mode 100644 index 00000000..2b35f141 --- /dev/null +++ b/.github/workflows/run-build-check.yml @@ -0,0 +1,35 @@ +name: Run build check + +on: + push: + branches: + - '*' + tags: + - 'v*' + + pull_request: + types: [opened, edited] + branches: + - '*' + +jobs: + build: + name: Run yarn build + runs-on: ubuntu-22.04 + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 20 + + - name: Install dependencies + run: yarn install + + - name: Build + run: yarn build + + diff --git a/.github/workflows/run-eslint-check.yml b/.github/workflows/run-eslint-check.yml index e08872b3..9b80e964 100644 --- a/.github/workflows/run-eslint-check.yml +++ b/.github/workflows/run-eslint-check.yml @@ -6,6 +6,11 @@ on: - '*' tags: - 'v*' + + pull_request: + types: [opened, edited] + branches: + - '*' jobs: eslint: From b7e93586dd3c2ed505bd9242bca23058238f2072 Mon Sep 17 00:00:00 2001 From: GoldenJaden Date: Tue, 24 Oct 2023 23:43:53 +0300 Subject: [PATCH 16/40] Workflows for API On pull/push: run build check, eslint check, tests. On push to main additionally deploy. --- .github/workflows/{Deploy-API.yml => deploy-API.yml} | 7 +------ .github/workflows/run-build-check.yml | 3 --- .github/workflows/run-tests.yml | 7 +++++++ 3 files changed, 8 insertions(+), 9 deletions(-) rename .github/workflows/{Deploy-API.yml => deploy-API.yml} (94%) diff --git a/.github/workflows/Deploy-API.yml b/.github/workflows/deploy-API.yml similarity index 94% rename from .github/workflows/Deploy-API.yml rename to .github/workflows/deploy-API.yml index a9d20e51..038f99b9 100644 --- a/.github/workflows/Deploy-API.yml +++ b/.github/workflows/deploy-API.yml @@ -3,14 +3,9 @@ name: Deploy API on: push: branches: - - '*' + - 'main' tags: - 'v*' - - pull_request: - types: [opened, edited] - branches: - - '*' env: REGISTRY: ghcr.io diff --git a/.github/workflows/run-build-check.yml b/.github/workflows/run-build-check.yml index 2b35f141..d670231f 100644 --- a/.github/workflows/run-build-check.yml +++ b/.github/workflows/run-build-check.yml @@ -26,9 +26,6 @@ jobs: with: node-version: 20 - - name: Install dependencies - run: yarn install - - name: Build run: yarn build diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 8f72e1a0..d3ae72da 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,7 +1,14 @@ name: Run tests on: + push: + branches: + - '*' + tags: + - 'v*' + pull_request: + types: [opened, edited] branches: - '*' From 00acf42a7fd7aa1f805abea3cb699cb33339571a Mon Sep 17 00:00:00 2001 From: GoldenJaden Date: Tue, 24 Oct 2023 23:46:08 +0300 Subject: [PATCH 17/40] fix Added installing dependencies for build check --- .github/workflows/run-build-check.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/run-build-check.yml b/.github/workflows/run-build-check.yml index d670231f..2b35f141 100644 --- a/.github/workflows/run-build-check.yml +++ b/.github/workflows/run-build-check.yml @@ -26,6 +26,9 @@ jobs: with: node-version: 20 + - name: Install dependencies + run: yarn install + - name: Build run: yarn build From aa12af445031eeea814e681557913a5f08b2badb Mon Sep 17 00:00:00 2001 From: e11sy Date: Thu, 26 Oct 2023 17:55:37 +0300 Subject: [PATCH 18/40] access_token is undefined - trying to fix test problem with auth --- src/presentation/http/router/auth.ts | 5 ++++ src/presentation/http/router/noteList.test.ts | 28 +++++++++++++++---- src/tests/test-data/userSessions.json | 2 +- src/tests/test-data/users.json | 7 +++++ 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/presentation/http/router/auth.ts b/src/presentation/http/router/auth.ts index c3775c7d..3e146b47 100644 --- a/src/presentation/http/router/auth.ts +++ b/src/presentation/http/router/auth.ts @@ -42,6 +42,11 @@ const AuthRouter: FastifyPluginCallback = (fastify, opts, don }>('/', async (request, reply) => { const { token } = request.body; + console.log('token======================================'); + console.log(token); + throw new Error(token); + console.log('======================================'); + const userSession = await opts.authService.verifyRefreshToken(token); /** diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index 1853a696..36480a3f 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -2,11 +2,27 @@ import userSessions from '@tests/test-data/userSessions.json'; import { describe, test, expect } from 'vitest'; const refresh_token = userSessions[0]['refresh_token']; + + +console.log('ref============================================'); +console.log(refresh_token); +console.log('============================================'); + const access_token = await global.api?.fakeRequest({ + headers : { + 'Content-Type': 'application/json', + }, method: 'POST', - url: `/auth/?token=${refresh_token}`, + url: '/auth', + body : JSON.stringify({ token : refresh_token }), + // body : refresh_token, }); + +console.log('acc============================================'); +console.log(access_token); +console.log('============================================'); + describe('NoteList API', () => { describe('GET /notes?page', () => { test('Returns noteList with specified lenght (not for last page)', async () => { @@ -19,7 +35,7 @@ describe('NoteList API', () => { headers: { authorization: `Bearer ${access_token}`, }, - url: `/notes/&page=${pageNumber}`, + url: `/notes&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -39,7 +55,7 @@ describe('NoteList API', () => { headers: { authorization: `Bearer ${access_token}`, }, - url: `/notes/&page=${pageNumber}`, + url: `/notes&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -58,7 +74,7 @@ describe('NoteList API', () => { headers: { authorization: `Bearer ${access_token}`, }, - url: `/notes/&page=${pageNumber}`, + url: `/notes&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -79,7 +95,7 @@ describe('NoteList API', () => { headers: { authorization: `Bearer ${access_token}`, }, - url: `/notes/&page=${pageNumber}`, + url: `/notes&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expextedStatus); @@ -94,7 +110,7 @@ describe('NoteList API', () => { headers: { authorization: `Bearer ${access_token}`, }, - url: `/notes/&page=${pageNumber}`, + url: `/notes&page=${pageNumber}`, }); expect(response?.statusCode).toBe(expextedStatus); diff --git a/src/tests/test-data/userSessions.json b/src/tests/test-data/userSessions.json index 57693776..c35b9cba 100644 --- a/src/tests/test-data/userSessions.json +++ b/src/tests/test-data/userSessions.json @@ -3,6 +3,6 @@ "id" : 1, "user_id" : 4, "refresh_token" : "IqrTkSKmel", - "refresh_token_expites_at" : "2023-11-21 19:19:40.911+03" + "refresh_token_expites_at" : "2025-11-21 19:19:40.911+03" } ] \ No newline at end of file diff --git a/src/tests/test-data/users.json b/src/tests/test-data/users.json index 2b993510..b92b4931 100644 --- a/src/tests/test-data/users.json +++ b/src/tests/test-data/users.json @@ -4,5 +4,12 @@ "email": "a@a.com", "name": "Test user 1", "created_at": "2023-10-16T13:49:19.000Z" + }, + { + "id": 4, + "email": "Egoramurin@gmail.com", + "name": "Егор Амурин", + "created_at": "2023-10-22 19:19:40.892+03", + "photo": "https://lh3.googleusercontent.com/a/ACg8ocL9_uBaC7XMFhosJZfIkDLC8tTm1GhtbvDfSbjf9eI2=s96-c" } ] From b4704d8f80b603aaf4095731bfaa2ca1ecf103b0 Mon Sep 17 00:00:00 2001 From: e11sy Date: Thu, 26 Oct 2023 18:03:03 +0300 Subject: [PATCH 19/40] deleted unwanted changes - deleted error for tests throw - lint fixes --- src/presentation/http/router/auth.ts | 1 - src/presentation/http/router/noteList.test.ts | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/presentation/http/router/auth.ts b/src/presentation/http/router/auth.ts index 3e146b47..6addd2b1 100644 --- a/src/presentation/http/router/auth.ts +++ b/src/presentation/http/router/auth.ts @@ -44,7 +44,6 @@ const AuthRouter: FastifyPluginCallback = (fastify, opts, don console.log('token======================================'); console.log(token); - throw new Error(token); console.log('======================================'); const userSession = await opts.authService.verifyRefreshToken(token); diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index 36480a3f..96678c23 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -9,9 +9,6 @@ console.log(refresh_token); console.log('============================================'); const access_token = await global.api?.fakeRequest({ - headers : { - 'Content-Type': 'application/json', - }, method: 'POST', url: '/auth', body : JSON.stringify({ token : refresh_token }), From 3c3e39af8044880812305503fe7649d7b1ef7683 Mon Sep 17 00:00:00 2001 From: elizachi Date: Fri, 27 Oct 2023 16:26:45 +0300 Subject: [PATCH 20/40] Testcases were written for the get note by id method New data for tests were added to the test database --- src/presentation/http/router/note.test.ts | 59 +++++++++++++++++++++++ src/tests/test-data/notes-settings.json | 14 ++++++ src/tests/test-data/notes.json | 18 +++++++ 3 files changed, 91 insertions(+) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index c7c1ab1c..bb145e27 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -50,3 +50,62 @@ describe('Note API', () => { }); }); }); + +describe('Note API', () => { + describe('GET note/:notePublicId ', () => { + test('Returns note by public id', async () => { + const expectedStatus = 200; + + const expectedNote = { + 'id': 2, + 'publicId': 'testnote11', + 'creatorId': 1, + 'content': null, + 'createdAt': '2023-10-16T13:49:19.000Z', + 'updatedAt': '2023-10-16T13:49:19.000Z', + }; + + const response = await global.api?.fakeRequest({ + method: 'GET', + url: '/note/testnote11', + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toStrictEqual(expectedNote); + }); + + test('Returns 403 when permission denied', async () => { + const expectedStatus = 403; + + const response = await global.api?.fakeRequest({ + method: 'GET', + url: '/note/testnote22', + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toStrictEqual({ message: 'Permission denied' }); + }); + + test('Returns 406 when public id incorrect', async () => { + const expectedStatus = 406; + + const response = await global.api?.fakeRequest({ + method: 'GET', + url: '/note/wrong_1_id', + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toStrictEqual({ message: 'Note not found' }); + }); + + }); +}); \ No newline at end of file diff --git a/src/tests/test-data/notes-settings.json b/src/tests/test-data/notes-settings.json index 822b37c9..299ce98e 100644 --- a/src/tests/test-data/notes-settings.json +++ b/src/tests/test-data/notes-settings.json @@ -4,5 +4,19 @@ "note_id": 1, "custom_hostname": "codex.so", "is_public": true + }, + + { + "id": 2, + "note_id": 2, + "custom_hostname": "codex.so", + "is_public": true + }, + + { + "id": 3, + "note_id": 3, + "custom_hostname": "codex.so", + "is_public": false } ] diff --git a/src/tests/test-data/notes.json b/src/tests/test-data/notes.json index 4fc09cbf..49005188 100644 --- a/src/tests/test-data/notes.json +++ b/src/tests/test-data/notes.json @@ -6,5 +6,23 @@ "content": [], "created_at": "2023-10-16T13:49:19.000Z", "updated_at": "2023-10-16T13:49:19.000Z" + }, + + { + "id": 2, + "public_id": "testnote11", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:19.000Z", + "updated_at": "2023-10-16T13:49:19.000Z" + }, + + { + "id": 3, + "public_id": "testnote22", + "creator_id": 1, + "content": [], + "created_at": "2023-10-16T13:49:19.000Z", + "updated_at": "2023-10-16T13:49:19.000Z" } ] From 29c81a5e3084b0223c38f8a39827242a93552071 Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Sat, 28 Oct 2023 00:13:34 +0300 Subject: [PATCH 21/40] fix: test auth --- src/presentation/http/router/auth.ts | 5 -- src/presentation/http/router/noteList.test.ts | 84 ++++++++++++------- src/tests/test-data/userSessions.json | 4 +- src/tests/utils/insert-data.ts | 2 +- vitest.config.js | 1 + 5 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/presentation/http/router/auth.ts b/src/presentation/http/router/auth.ts index 6addd2b1..d77bc231 100644 --- a/src/presentation/http/router/auth.ts +++ b/src/presentation/http/router/auth.ts @@ -41,11 +41,6 @@ const AuthRouter: FastifyPluginCallback = (fastify, opts, don Reply: AuthSession | ErrorResponse; }>('/', async (request, reply) => { const { token } = request.body; - - console.log('token======================================'); - console.log(token); - console.log('======================================'); - const userSession = await opts.authService.verifyRefreshToken(token); /** diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index 96678c23..91cac554 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -1,28 +1,51 @@ +import type AuthSession from '@domain/entities/authSession'; import userSessions from '@tests/test-data/userSessions.json'; -import { describe, test, expect } from 'vitest'; - -const refresh_token = userSessions[0]['refresh_token']; - - -console.log('ref============================================'); -console.log(refresh_token); -console.log('============================================'); - -const access_token = await global.api?.fakeRequest({ - method: 'POST', - url: '/auth', - body : JSON.stringify({ token : refresh_token }), - // body : refresh_token, -}); +import { describe, test, expect, beforeAll } from 'vitest'; + + +/** + * Access token that will be used for Auhorization header + */ +let accessToken = ''; + +/** + * Util for authorization + * + * @param refreshToken - refresh token. There should be a user session with this refresh token in database + * @todo Move this function to tests/utils + */ +async function authorize(refreshToken: string): Promise { + const response = await global.api?.fakeRequest({ + method: 'POST', + url: '/auth', + headers: { + // eslint-disable-next-line @typescript-eslint/naming-convention + 'Content-Type': 'application/json', + }, + body : JSON.stringify({ token : refreshToken }), + }); + const body: AuthSession = response?.body !== undefined ? JSON.parse(response?.body) : {}; -console.log('acc============================================'); -console.log(access_token); -console.log('============================================'); + return body.accessToken; +} describe('NoteList API', () => { + beforeAll(async () => { + /** + * Authorize using refresh token and POST /auth + */ + const refreshToken = userSessions[0]['refresh_token']; + + try { + accessToken = await authorize(refreshToken); + } catch (error) { + console.log('Test Authorization failed', error); + } + }); + describe('GET /notes?page', () => { - test('Returns noteList with specified lenght (not for last page)', async () => { + test('Returns noteList with specified length (not for last page)', async () => { const expectedStatus = 200; const portionSize = 30; const pageNumber = 1; @@ -30,9 +53,9 @@ describe('NoteList API', () => { const response = await global.api?.fakeRequest({ method: 'GET', headers: { - authorization: `Bearer ${access_token}`, + authorization: `Bearer ${accessToken}`, }, - url: `/notes&page=${pageNumber}`, + url: `/notes?page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -50,9 +73,9 @@ describe('NoteList API', () => { const response = await global.api?.fakeRequest({ method: 'GET', headers: { - authorization: `Bearer ${access_token}`, + authorization: `Bearer ${accessToken}`, }, - url: `/notes&page=${pageNumber}`, + url: `/notes?page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -66,12 +89,15 @@ describe('NoteList API', () => { const expectedStatus = 200; const pageNumber = 3; + console.log('accessToken', accessToken); + + const response = await global.api?.fakeRequest({ method: 'GET', headers: { - authorization: `Bearer ${access_token}`, + authorization: `Bearer ${accessToken}`, }, - url: `/notes&page=${pageNumber}`, + url: `/notes?page=${pageNumber}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -90,9 +116,9 @@ describe('NoteList API', () => { const response = await global.api?.fakeRequest({ method: 'GET', headers: { - authorization: `Bearer ${access_token}`, + authorization: `Bearer ${accessToken}`, }, - url: `/notes&page=${pageNumber}`, + url: `/notes?page=${pageNumber}`, }); expect(response?.statusCode).toBe(expextedStatus); @@ -105,9 +131,9 @@ describe('NoteList API', () => { const response = await global.api?.fakeRequest({ method: 'GET', headers: { - authorization: `Bearer ${access_token}`, + authorization: `Bearer ${accessToken}`, }, - url: `/notes&page=${pageNumber}`, + url: `/notes?page=${pageNumber}`, }); expect(response?.statusCode).toBe(expextedStatus); diff --git a/src/tests/test-data/userSessions.json b/src/tests/test-data/userSessions.json index c35b9cba..a9d26410 100644 --- a/src/tests/test-data/userSessions.json +++ b/src/tests/test-data/userSessions.json @@ -3,6 +3,6 @@ "id" : 1, "user_id" : 4, "refresh_token" : "IqrTkSKmel", - "refresh_token_expites_at" : "2025-11-21 19:19:40.911+03" + "refresh_token_expires_at" : "2025-11-21 19:19:40.911+03" } -] \ No newline at end of file +] diff --git a/src/tests/utils/insert-data.ts b/src/tests/utils/insert-data.ts index d9f134e8..76140a7c 100644 --- a/src/tests/utils/insert-data.ts +++ b/src/tests/utils/insert-data.ts @@ -22,7 +22,7 @@ async function insertUsers(db: SequelizeOrm): Promise { */ async function insertUserSessions(db: SequelizeOrm): Promise { for (const userSession of userSessions) { - await db.connection.query(`INSERT INTO public.user_sessions (id, "user_id", "refresh_token", "refresh_token_expires_at") VALUES (${userSession.id}, ${userSession.user_id}, '${userSession.refresh_token}', '${userSession.refresh_token_expites_at}')`); + await db.connection.query(`INSERT INTO public.user_sessions (id, "user_id", "refresh_token", "refresh_token_expires_at") VALUES (${userSession.id}, ${userSession.user_id}, '${userSession.refresh_token}', '${userSession.refresh_token_expires_at}')`); } } /** diff --git a/vitest.config.js b/vitest.config.js index 65930db4..6539b273 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -17,4 +17,5 @@ export default defineConfig({ '@tests/': '/src/tests/', }, }, + console: 'integratedTerminal', }); From 6cda6629a9b3518b4dc0144c495b2257c87272c8 Mon Sep 17 00:00:00 2001 From: elizachi Date: Sat, 28 Oct 2023 15:00:44 +0300 Subject: [PATCH 22/40] fix description and oublic id of the test cases --- src/presentation/http/router/note.test.ts | 16 +++++++--------- src/tests/test-data/notes.json | 4 ++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index bb145e27..499aac27 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -49,16 +49,14 @@ describe('Note API', () => { expect(body).toStrictEqual({ message: 'Note not found' }); }); }); -}); -describe('Note API', () => { describe('GET note/:notePublicId ', () => { - test('Returns note by public id', async () => { + test('Returns note by public id with 200 status', async () => { const expectedStatus = 200; const expectedNote = { 'id': 2, - 'publicId': 'testnote11', + 'publicId': 'Pq1T9vc23Q', 'creatorId': 1, 'content': null, 'createdAt': '2023-10-16T13:49:19.000Z', @@ -67,7 +65,7 @@ describe('Note API', () => { const response = await global.api?.fakeRequest({ method: 'GET', - url: '/note/testnote11', + url: '/note/Pq1T9vc23Q', }); expect(response?.statusCode).toBe(expectedStatus); @@ -77,12 +75,12 @@ describe('Note API', () => { expect(body).toStrictEqual(expectedNote); }); - test('Returns 403 when permission denied', async () => { + test('Returns 403 when public access is disabled in the note settings', async () => { const expectedStatus = 403; const response = await global.api?.fakeRequest({ method: 'GET', - url: '/note/testnote22', + url: '/note/73NdxFZ4k7', }); expect(response?.statusCode).toBe(expectedStatus); @@ -92,12 +90,12 @@ describe('Note API', () => { expect(body).toStrictEqual({ message: 'Permission denied' }); }); - test('Returns 406 when public id incorrect', async () => { + test('Returns 406 when the id contains incorrect characters', async () => { const expectedStatus = 406; const response = await global.api?.fakeRequest({ method: 'GET', - url: '/note/wrong_1_id', + url: '/note/PR0B_bmdSy', }); expect(response?.statusCode).toBe(expectedStatus); diff --git a/src/tests/test-data/notes.json b/src/tests/test-data/notes.json index 49005188..01f9b5e8 100644 --- a/src/tests/test-data/notes.json +++ b/src/tests/test-data/notes.json @@ -10,7 +10,7 @@ { "id": 2, - "public_id": "testnote11", + "public_id": "Pq1T9vc23Q", "creator_id": 1, "content": [], "created_at": "2023-10-16T13:49:19.000Z", @@ -19,7 +19,7 @@ { "id": 3, - "public_id": "testnote22", + "public_id": "73NdxFZ4k7", "creator_id": 1, "content": [], "created_at": "2023-10-16T13:49:19.000Z", From 73c090459e373fe0e73dab0a372d1de6f5c8ed8d Mon Sep 17 00:00:00 2001 From: elizachi Date: Mon, 30 Oct 2023 22:12:21 +0300 Subject: [PATCH 23/40] added path to the tests folder for importing test data url for a fake request define using the find method --- src/presentation/http/router/note.test.ts | 32 ++++++++++++++++------- tsconfig.json | 3 ++- vitest.config.js | 1 + 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index 499aac27..ce54b47e 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -1,6 +1,9 @@ import { describe, test, expect } from 'vitest'; +import notes from '@tests/test-data/notes.json'; +import noteSettings from '@tests/test-data/notes-settings.json'; + describe('Note API', () => { describe('GET note/resolve-hostname/:hostname ', () => { test('Returns note with specified hostname', async () => { @@ -78,16 +81,25 @@ describe('Note API', () => { test('Returns 403 when public access is disabled in the note settings', async () => { const expectedStatus = 403; - const response = await global.api?.fakeRequest({ - method: 'GET', - url: '/note/73NdxFZ4k7', - }); - - expect(response?.statusCode).toBe(expectedStatus); - - const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; - - expect(body).toStrictEqual({ message: 'Permission denied' }); + const notPublicNote = notes.find(note => { + const settings = noteSettings.find(ns => ns.note_id === note.id) + if(settings != undefined) + return settings.is_public === false + }) + + if(notPublicNote != undefined) { + const response = await global.api?.fakeRequest({ + method: 'GET', + url: `/note/${notPublicNote.public_id}`, + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toStrictEqual({ message: 'Permission denied' }); + } + }); test('Returns 406 when the id contains incorrect characters', async () => { diff --git a/tsconfig.json b/tsconfig.json index 93e90d57..bf0aea25 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,8 @@ "@presentation/*": ["presentation/*"], "@lib/*": ["lib/*"], "@domain/*": ["domain/*"], - "@repository/*": ["repository/*"] + "@repository/*": ["repository/*"], + "@tests/*": ["tests/*"] }, }, "include": [ diff --git a/vitest.config.js b/vitest.config.js index dcbc97f0..9f15b0f7 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -14,6 +14,7 @@ export default defineConfig({ '@lib/': '/src/lib/', '@domain/': '/src/domain/', '@repository/': '/src/repository/', + '@tests/': '/src/tests/' }, }, }); From c60049816406e06d0291be8562b463ec70a323fc Mon Sep 17 00:00:00 2001 From: elizachi Date: Tue, 31 Oct 2023 01:54:52 +0300 Subject: [PATCH 24/40] removed undefined check fix implicit arrange --- src/presentation/http/router/note.test.ts | 43 +++++++++++------------ vitest.config.js | 2 +- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index ce54b47e..c925bd0f 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -56,7 +56,7 @@ describe('Note API', () => { describe('GET note/:notePublicId ', () => { test('Returns note by public id with 200 status', async () => { const expectedStatus = 200; - + const expectedNote = { 'id': 2, 'publicId': 'Pq1T9vc23Q', @@ -81,33 +81,31 @@ describe('Note API', () => { test('Returns 403 when public access is disabled in the note settings', async () => { const expectedStatus = 403; - const notPublicNote = notes.find(note => { - const settings = noteSettings.find(ns => ns.note_id === note.id) - if(settings != undefined) - return settings.is_public === false - }) - - if(notPublicNote != undefined) { - const response = await global.api?.fakeRequest({ - method: 'GET', - url: `/note/${notPublicNote.public_id}`, - }); - - expect(response?.statusCode).toBe(expectedStatus); - - const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; - - expect(body).toStrictEqual({ message: 'Permission denied' }); - } - + const notPublicNote = notes.find(newNote => { + const settings = noteSettings.find(ns => ns.note_id === newNote.id); + + return settings!.is_public === false; + }); + + const response = await global.api?.fakeRequest({ + method: 'GET', + url: `/note/${notPublicNote!.public_id}`, + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toStrictEqual({ message: 'Permission denied' }); }); test('Returns 406 when the id contains incorrect characters', async () => { const expectedStatus = 406; + const nonexistentId = 'PR0B_bmdSy'; const response = await global.api?.fakeRequest({ method: 'GET', - url: '/note/PR0B_bmdSy', + url: `/note/${nonexistentId}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -116,6 +114,5 @@ describe('Note API', () => { expect(body).toStrictEqual({ message: 'Note not found' }); }); - }); -}); \ No newline at end of file +}); \ No newline at end of file diff --git a/vitest.config.js b/vitest.config.js index 9f15b0f7..65930db4 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -14,7 +14,7 @@ export default defineConfig({ '@lib/': '/src/lib/', '@domain/': '/src/domain/', '@repository/': '/src/repository/', - '@tests/': '/src/tests/' + '@tests/': '/src/tests/', }, }, }); From f8c340e8c4c7123d91c336ebf2d85be2a47b72d2 Mon Sep 17 00:00:00 2001 From: GoldenJaden Date: Tue, 31 Oct 2023 15:24:25 +0300 Subject: [PATCH 25/40] Quickfix: added synchronize trigger on PR to actions --- .github/workflows/run-build-check.yml | 2 +- .github/workflows/run-eslint-check.yml | 2 +- .github/workflows/run-tests.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-build-check.yml b/.github/workflows/run-build-check.yml index 2b35f141..7b2655b7 100644 --- a/.github/workflows/run-build-check.yml +++ b/.github/workflows/run-build-check.yml @@ -8,7 +8,7 @@ on: - 'v*' pull_request: - types: [opened, edited] + types: [opened, edited, synchronize] branches: - '*' diff --git a/.github/workflows/run-eslint-check.yml b/.github/workflows/run-eslint-check.yml index 9b80e964..54d18f0d 100644 --- a/.github/workflows/run-eslint-check.yml +++ b/.github/workflows/run-eslint-check.yml @@ -8,7 +8,7 @@ on: - 'v*' pull_request: - types: [opened, edited] + types: [opened, edited, synchronize] branches: - '*' diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d3ae72da..fba8f941 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,7 @@ on: - 'v*' pull_request: - types: [opened, edited] + types: [opened, edited, synchronize] branches: - '*' From ea093097aa78b1b5ba1c1a2f95f5b28ee937e63c Mon Sep 17 00:00:00 2001 From: e11sy Date: Wed, 1 Nov 2023 02:23:35 +0300 Subject: [PATCH 26/40] quickFix(tests): logic fixes - noteList test logic fixed (authorization for specific user needed) --- src/presentation/http/router/noteList.test.ts | 10 +- src/tests/test-data/notes.json | 98 +++++++++---------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index 91cac554..cce69aea 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -62,12 +62,12 @@ describe('NoteList API', () => { const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; - expect(body).toHaveLength(portionSize); + expect(body.items).toHaveLength(portionSize); }); test('Returns noteList with specified lenght (for last page)', async () => { const expectedStatus = 200; - const portionSize = 20; + const portionSize = 19; const pageNumber = 2; const response = await global.api?.fakeRequest({ @@ -82,7 +82,7 @@ describe('NoteList API', () => { const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; - expect(body).toHaveLength(portionSize); + expect(body.items).toHaveLength(portionSize); }); test('Returns noteList with no items if it has no notes', async () => { @@ -104,8 +104,8 @@ describe('NoteList API', () => { const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; - expect(body).toEqual([]); - expect(body).toHaveLength(0); + expect(body).toEqual( { items : [] } ); + expect(body.items).toHaveLength(0); }); test('Returns 400 when page < 0', async () => { diff --git a/src/tests/test-data/notes.json b/src/tests/test-data/notes.json index 32a5c1c0..66475967 100644 --- a/src/tests/test-data/notes.json +++ b/src/tests/test-data/notes.json @@ -10,7 +10,7 @@ { "id": 2, "public_id": "99999902", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:2.000Z", "updated_at": "2023-10-16T13:49:2.000Z" @@ -18,7 +18,7 @@ { "id": 3, "public_id": "99999903", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:3.000Z", "updated_at": "2023-10-16T13:49:3.000Z" @@ -26,7 +26,7 @@ { "id": 4, "public_id": "99999904", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:4.000Z", "updated_at": "2023-10-16T13:49:4.000Z" @@ -34,7 +34,7 @@ { "id": 5, "public_id": "99999905", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:5.000Z", "updated_at": "2023-10-16T13:49:5.000Z" @@ -42,7 +42,7 @@ { "id": 6, "public_id": "99999906", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:6.000Z", "updated_at": "2023-10-16T13:49:6.000Z" @@ -50,7 +50,7 @@ { "id": 7, "public_id": "99999907", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:7.000Z", "updated_at": "2023-10-16T13:49:7.000Z" @@ -58,7 +58,7 @@ { "id": 8, "public_id": "99999908", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:8.000Z", "updated_at": "2023-10-16T13:49:8.000Z" @@ -66,7 +66,7 @@ { "id": 9, "public_id": "99999909", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:9.000Z", "updated_at": "2023-10-16T13:49:9.000Z" @@ -74,7 +74,7 @@ { "id": 10, "public_id": "99999910", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:10.000Z", "updated_at": "2023-10-16T13:49:10.000Z" @@ -82,7 +82,7 @@ { "id": 11, "public_id": "99999911", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:11.000Z", "updated_at": "2023-10-16T13:49:11.000Z" @@ -90,7 +90,7 @@ { "id": 12, "public_id": "99999912", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:12.000Z", "updated_at": "2023-10-16T13:49:12.000Z" @@ -98,7 +98,7 @@ { "id": 13, "public_id": "99999913", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:13.000Z", "updated_at": "2023-10-16T13:49:13.000Z" @@ -106,7 +106,7 @@ { "id": 14, "public_id": "99999914", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:14.000Z", "updated_at": "2023-10-16T13:49:14.000Z" @@ -114,7 +114,7 @@ { "id": 15, "public_id": "99999915", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:15.000Z", "updated_at": "2023-10-16T13:49:15.000Z" @@ -122,7 +122,7 @@ { "id": 16, "public_id": "99999916", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:16.000Z", "updated_at": "2023-10-16T13:49:16.000Z" @@ -130,7 +130,7 @@ { "id": 17, "public_id": "99999917", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:17.000Z", "updated_at": "2023-10-16T13:49:17.000Z" @@ -138,7 +138,7 @@ { "id": 18, "public_id": "99999918", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:18.000Z", "updated_at": "2023-10-16T13:49:18.000Z" @@ -146,7 +146,7 @@ { "id": 19, "public_id": "99999919", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:19.000Z", "updated_at": "2023-10-16T13:49:19.000Z" @@ -154,7 +154,7 @@ { "id": 20, "public_id": "99999920", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:20.000Z", "updated_at": "2023-10-16T13:49:20.000Z" @@ -162,7 +162,7 @@ { "id": 21, "public_id": "99999921", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:21.000Z", "updated_at": "2023-10-16T13:49:21.000Z" @@ -170,7 +170,7 @@ { "id": 22, "public_id": "99999922", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:22.000Z", "updated_at": "2023-10-16T13:49:22.000Z" @@ -178,7 +178,7 @@ { "id": 23, "public_id": "99999923", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:23.000Z", "updated_at": "2023-10-16T13:49:23.000Z" @@ -186,7 +186,7 @@ { "id": 24, "public_id": "99999924", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:24.000Z", "updated_at": "2023-10-16T13:49:24.000Z" @@ -194,7 +194,7 @@ { "id": 25, "public_id": "99999925", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:25.000Z", "updated_at": "2023-10-16T13:49:25.000Z" @@ -202,7 +202,7 @@ { "id": 26, "public_id": "99999926", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:26.000Z", "updated_at": "2023-10-16T13:49:26.000Z" @@ -210,7 +210,7 @@ { "id": 27, "public_id": "99999927", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:27.000Z", "updated_at": "2023-10-16T13:49:27.000Z" @@ -218,7 +218,7 @@ { "id": 28, "public_id": "99999928", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:28.000Z", "updated_at": "2023-10-16T13:49:28.000Z" @@ -226,7 +226,7 @@ { "id": 29, "public_id": "99999929", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:29.000Z", "updated_at": "2023-10-16T13:49:29.000Z" @@ -234,7 +234,7 @@ { "id": 30, "public_id": "99999930", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:30.000Z", "updated_at": "2023-10-16T13:49:30.000Z" @@ -242,7 +242,7 @@ { "id": 31, "public_id": "99999931", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:31.000Z", "updated_at": "2023-10-16T13:49:31.000Z" @@ -250,7 +250,7 @@ { "id": 32, "public_id": "99999932", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:32.000Z", "updated_at": "2023-10-16T13:49:32.000Z" @@ -258,7 +258,7 @@ { "id": 33, "public_id": "99999933", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:33.000Z", "updated_at": "2023-10-16T13:49:33.000Z" @@ -266,7 +266,7 @@ { "id": 34, "public_id": "99999934", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:34.000Z", "updated_at": "2023-10-16T13:49:34.000Z" @@ -274,7 +274,7 @@ { "id": 35, "public_id": "99999935", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:35.000Z", "updated_at": "2023-10-16T13:49:35.000Z" @@ -282,7 +282,7 @@ { "id": 36, "public_id": "99999936", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:36.000Z", "updated_at": "2023-10-16T13:49:36.000Z" @@ -290,7 +290,7 @@ { "id": 37, "public_id": "99999937", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:37.000Z", "updated_at": "2023-10-16T13:49:37.000Z" @@ -298,7 +298,7 @@ { "id": 38, "public_id": "99999938", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:38.000Z", "updated_at": "2023-10-16T13:49:38.000Z" @@ -306,7 +306,7 @@ { "id": 39, "public_id": "99999939", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:39.000Z", "updated_at": "2023-10-16T13:49:39.000Z" @@ -314,7 +314,7 @@ { "id": 40, "public_id": "99999940", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:40.000Z", "updated_at": "2023-10-16T13:49:40.000Z" @@ -322,7 +322,7 @@ { "id": 41, "public_id": "99999941", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:41.000Z", "updated_at": "2023-10-16T13:49:41.000Z" @@ -330,7 +330,7 @@ { "id": 42, "public_id": "99999942", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:42.000Z", "updated_at": "2023-10-16T13:49:42.000Z" @@ -338,7 +338,7 @@ { "id": 43, "public_id": "99999943", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:43.000Z", "updated_at": "2023-10-16T13:49:43.000Z" @@ -346,7 +346,7 @@ { "id": 44, "public_id": "99999944", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:44.000Z", "updated_at": "2023-10-16T13:49:44.000Z" @@ -354,7 +354,7 @@ { "id": 45, "public_id": "99999945", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:45.000Z", "updated_at": "2023-10-16T13:49:45.000Z" @@ -362,7 +362,7 @@ { "id": 46, "public_id": "99999946", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:46.000Z", "updated_at": "2023-10-16T13:49:46.000Z" @@ -370,7 +370,7 @@ { "id": 47, "public_id": "99999947", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:47.000Z", "updated_at": "2023-10-16T13:49:47.000Z" @@ -378,7 +378,7 @@ { "id": 48, "public_id": "99999948", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:48.000Z", "updated_at": "2023-10-16T13:49:48.000Z" @@ -386,7 +386,7 @@ { "id": 49, "public_id": "99999949", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:49.000Z", "updated_at": "2023-10-16T13:49:49.000Z" @@ -394,7 +394,7 @@ { "id": 50, "public_id": "99999950", - "creator_id": 1, + "creator_id": 4, "content": [], "created_at": "2023-10-16T13:49:50.000Z", "updated_at": "2023-10-16T13:49:50.000Z" From eef6a67f9582e0616d1f2c6770211afc760591bd Mon Sep 17 00:00:00 2001 From: IvanS Date: Wed, 1 Nov 2023 14:32:26 +0300 Subject: [PATCH 27/40] push trigger fix --- .github/workflows/run-build-check.yml | 6 ++---- .github/workflows/run-eslint-check.yml | 8 +++----- .github/workflows/run-tests.yml | 8 +++----- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.github/workflows/run-build-check.yml b/.github/workflows/run-build-check.yml index 7b2655b7..02d5fd6b 100644 --- a/.github/workflows/run-build-check.yml +++ b/.github/workflows/run-build-check.yml @@ -3,14 +3,12 @@ name: Run build check on: push: branches: - - '*' + - 'main' tags: - 'v*' pull_request: - types: [opened, edited, synchronize] - branches: - - '*' + types: [opened, synchronize] jobs: build: diff --git a/.github/workflows/run-eslint-check.yml b/.github/workflows/run-eslint-check.yml index 54d18f0d..56011601 100644 --- a/.github/workflows/run-eslint-check.yml +++ b/.github/workflows/run-eslint-check.yml @@ -3,14 +3,12 @@ name: Run ESlint check on: push: branches: - - '*' + - 'main' tags: - 'v*' - + pull_request: - types: [opened, edited, synchronize] - branches: - - '*' + types: [opened, synchronize] jobs: eslint: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index fba8f941..6cd13c28 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -3,14 +3,12 @@ name: Run tests on: push: branches: - - '*' + - 'main' tags: - 'v*' - + pull_request: - types: [opened, edited, synchronize] - branches: - - '*' + types: [opened, synchronize] jobs: tests: From d30bde383e61be00dc4a62fe27a74cd1a9b4cf41 Mon Sep 17 00:00:00 2001 From: IvanS Date: Wed, 1 Nov 2023 15:01:31 +0300 Subject: [PATCH 28/40] added reopened trigger --- .github/workflows/run-build-check.yml | 2 +- .github/workflows/run-eslint-check.yml | 2 +- .github/workflows/run-tests.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-build-check.yml b/.github/workflows/run-build-check.yml index 02d5fd6b..947a963b 100644 --- a/.github/workflows/run-build-check.yml +++ b/.github/workflows/run-build-check.yml @@ -8,7 +8,7 @@ on: - 'v*' pull_request: - types: [opened, synchronize] + types: [opened, synchronize, reopened] jobs: build: diff --git a/.github/workflows/run-eslint-check.yml b/.github/workflows/run-eslint-check.yml index 56011601..2dcc99d0 100644 --- a/.github/workflows/run-eslint-check.yml +++ b/.github/workflows/run-eslint-check.yml @@ -8,7 +8,7 @@ on: - 'v*' pull_request: - types: [opened, synchronize] + types: [opened, synchronize, reopened] jobs: eslint: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6cd13c28..9368da48 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,7 @@ on: - 'v*' pull_request: - types: [opened, synchronize] + types: [opened, synchronize, reopened] jobs: tests: From d3e68a04c598a7daed22b51143d0c6714514668f Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 01:25:47 +0300 Subject: [PATCH 29/40] Added another test case, but it doesn't work yet. Need to decide what to do with the note creator. --- src/presentation/http/router/note.test.ts | 47 +++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index c925bd0f..df210daf 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -3,6 +3,7 @@ import { describe, test, expect } from 'vitest'; import notes from '@tests/test-data/notes.json'; import noteSettings from '@tests/test-data/notes-settings.json'; +// import users from '@tests/test-data/notes.json'; describe('Note API', () => { describe('GET note/resolve-hostname/:hostname ', () => { @@ -54,8 +55,13 @@ describe('Note API', () => { }); describe('GET note/:notePublicId ', () => { - test('Returns note by public id with 200 status', async () => { + test('Returns note by public id with 200 status ' + + 'when note is publicly available', async () => { const expectedStatus = 200; + const correctID = 'Pq1T9vc23Q'; + + // TODO API should not return internal id and "publicId". + // It should return only "id" which is public id. Not implemented yet. const expectedNote = { 'id': 2, @@ -68,7 +74,7 @@ describe('Note API', () => { const response = await global.api?.fakeRequest({ method: 'GET', - url: '/note/Pq1T9vc23Q', + url: `/note/${correctID}`, }); expect(response?.statusCode).toBe(expectedStatus); @@ -78,7 +84,42 @@ describe('Note API', () => { expect(body).toStrictEqual(expectedNote); }); - test('Returns 403 when public access is disabled in the note settings', async () => { + test('Returns note by public id with 200 status ' + + 'when access is disabled in the note settings, but user is creator', async () => { + const expectedStatus = 200; + + // TODO add authorization or something + // else so that the user can be recognized as the author of the note + + const authorsPrivatNote = notes.find(newNote => { + const settings = noteSettings.find(ns => ns.note_id === newNote.id); + + return settings!.is_public === false; + }); + + const authorsExpectedNote = { + 'id': 3, + 'publicId': '73NdxFZ4k7', + 'creatorId': 1, + 'content': null, + 'createdAt': '2023-10-16T13:49:19.000Z', + 'updatedAt': '2023-10-16T13:49:19.000Z', + }; + + const response = await global.api?.fakeRequest({ + method: 'GET', + url: `/note/${authorsPrivatNote!.public_id}`, + }); + + expect(response?.statusCode).toBe(expectedStatus); + + const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + + expect(body).toStrictEqual(authorsExpectedNote); + }); + + test('Returns 403 when public access is disabled in the note settings,' + + 'user is not creator of the note', async () => { const expectedStatus = 403; const notPublicNote = notes.find(newNote => { From bfc45151ced55a8a01024b185a7b5ed1cb1568ff Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 14:05:26 +0300 Subject: [PATCH 30/40] Add TODO position from review --- src/presentation/http/router/note.test.ts | 35 ++--------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index df210daf..3aa7b05c 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -84,39 +84,8 @@ describe('Note API', () => { expect(body).toStrictEqual(expectedNote); }); - test('Returns note by public id with 200 status ' + - 'when access is disabled in the note settings, but user is creator', async () => { - const expectedStatus = 200; - - // TODO add authorization or something - // else so that the user can be recognized as the author of the note - - const authorsPrivatNote = notes.find(newNote => { - const settings = noteSettings.find(ns => ns.note_id === newNote.id); - - return settings!.is_public === false; - }); - - const authorsExpectedNote = { - 'id': 3, - 'publicId': '73NdxFZ4k7', - 'creatorId': 1, - 'content': null, - 'createdAt': '2023-10-16T13:49:19.000Z', - 'updatedAt': '2023-10-16T13:49:19.000Z', - }; - - const response = await global.api?.fakeRequest({ - method: 'GET', - url: `/note/${authorsPrivatNote!.public_id}`, - }); - - expect(response?.statusCode).toBe(expectedStatus); - - const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; - - expect(body).toStrictEqual(authorsExpectedNote); - }); + // TODO add authorization or something + // else so that the user can be recognized as the author of the note test('Returns 403 when public access is disabled in the note settings,' + 'user is not creator of the note', async () => { From aca5ee8853f325db9dec1826b38accb777baf772 Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 14:15:04 +0300 Subject: [PATCH 31/40] Use response?.json() instead of manually parsing body --- src/presentation/http/router/note.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index 3aa7b05c..bca1fd6b 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -79,7 +79,7 @@ describe('Note API', () => { expect(response?.statusCode).toBe(expectedStatus); - const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + const body = response?.json(); expect(body).toStrictEqual(expectedNote); }); @@ -87,7 +87,7 @@ describe('Note API', () => { // TODO add authorization or something // else so that the user can be recognized as the author of the note - test('Returns 403 when public access is disabled in the note settings,' + + test('Returns 403 when public access is disabled in the note settings, ' + 'user is not creator of the note', async () => { const expectedStatus = 403; @@ -104,7 +104,7 @@ describe('Note API', () => { expect(response?.statusCode).toBe(expectedStatus); - const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + const body = response?.json(); expect(body).toStrictEqual({ message: 'Permission denied' }); }); @@ -120,7 +120,7 @@ describe('Note API', () => { expect(response?.statusCode).toBe(expectedStatus); - const body = response?.body !== undefined ? JSON.parse(response?.body) : {}; + const body = response?.json(); expect(body).toStrictEqual({ message: 'Note not found' }); }); From 00bc8b18b0ab716ef8d2438e5b13d05bf0d67d3c Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 17:44:12 +0300 Subject: [PATCH 32/40] Changed the description of the test cases, added @todo --- src/presentation/http/router/note.test.ts | 34 +++++++++-------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index bca1fd6b..a8e3f9cf 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -3,7 +3,6 @@ import { describe, test, expect } from 'vitest'; import notes from '@tests/test-data/notes.json'; import noteSettings from '@tests/test-data/notes-settings.json'; -// import users from '@tests/test-data/notes.json'; describe('Note API', () => { describe('GET note/resolve-hostname/:hostname ', () => { @@ -55,13 +54,14 @@ describe('Note API', () => { }); describe('GET note/:notePublicId ', () => { - test('Returns note by public id with 200 status ' + - 'when note is publicly available', async () => { + test('Returns note by public id with 200 status when note is publicly available', async () => { const expectedStatus = 200; const correctID = 'Pq1T9vc23Q'; - // TODO API should not return internal id and "publicId". - // It should return only "id" which is public id. Not implemented yet. + /** + * @todo API should not return internal id and "publicId". + * It should return only "id" which is public id. Not implemented yet. + */ const expectedNote = { 'id': 2, @@ -79,16 +79,10 @@ describe('Note API', () => { expect(response?.statusCode).toBe(expectedStatus); - const body = response?.json(); - - expect(body).toStrictEqual(expectedNote); + expect(response?.json()).toStrictEqual(expectedNote); }); - // TODO add authorization or something - // else so that the user can be recognized as the author of the note - - test('Returns 403 when public access is disabled in the note settings, ' + - 'user is not creator of the note', async () => { + test('Returns 403 when public access is disabled, user is not creator of the note', async () => { const expectedStatus = 403; const notPublicNote = notes.find(newNote => { @@ -104,14 +98,12 @@ describe('Note API', () => { expect(response?.statusCode).toBe(expectedStatus); - const body = response?.json(); - - expect(body).toStrictEqual({ message: 'Permission denied' }); + expect(response?.json()).toStrictEqual({ message: 'Permission denied' }); }); - test('Returns 406 when the id contains incorrect characters', async () => { + test('Returns 406 when the id does not exist', async () => { const expectedStatus = 406; - const nonexistentId = 'PR0B_bmdSy'; + const nonexistentId = 'PR0BrbmdSy'; const response = await global.api?.fakeRequest({ method: 'GET', @@ -120,9 +112,9 @@ describe('Note API', () => { expect(response?.statusCode).toBe(expectedStatus); - const body = response?.json(); - - expect(body).toStrictEqual({ message: 'Note not found' }); + expect(response?.json()).toStrictEqual({ message: 'Note not found' }); }); }); + + test.todo('Returns note by public id with 200 status when access is disabled, but user is creator'); }); \ No newline at end of file From 55fc668f91cc8cead52e9a495d25746c6098af59 Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 18:26:36 +0300 Subject: [PATCH 33/40] Added test case with id cintains incorrect characters --- src/presentation/http/router/note.test.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index a8e3f9cf..b479a535 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -103,7 +103,7 @@ describe('Note API', () => { test('Returns 406 when the id does not exist', async () => { const expectedStatus = 406; - const nonexistentId = 'PR0BrbmdSy'; + const nonexistentId = 'ishvm5qH84'; const response = await global.api?.fakeRequest({ method: 'GET', @@ -114,6 +114,20 @@ describe('Note API', () => { expect(response?.json()).toStrictEqual({ message: 'Note not found' }); }); + + test('Returns 406 when the id contains incorrect characters', async () => { + const expectedStatus = 406; + const incorrectCharId = 'PR^B{@,&S!'; + + const response = await global.api?.fakeRequest({ + method: 'GET', + url: `/note/${incorrectCharId}`, + }); + + expect(response?.statusCode).toBe(expectedStatus); + + expect(response?.json()).toStrictEqual({ message: 'Note not found' }); + }); }); test.todo('Returns note by public id with 200 status when access is disabled, but user is creator'); From d249467360f9c9227934748aecfa4f4d8f345398 Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 18:42:25 +0300 Subject: [PATCH 34/40] Fix test case and @todo description --- src/presentation/http/router/note.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index b479a535..7111795a 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -58,10 +58,7 @@ describe('Note API', () => { const expectedStatus = 200; const correctID = 'Pq1T9vc23Q'; - /** - * @todo API should not return internal id and "publicId". - * It should return only "id" which is public id. Not implemented yet. - */ + test.todo('API should not return internal id and "publicId". It should return only "id" which is public id.'); const expectedNote = { 'id': 2, @@ -115,7 +112,7 @@ describe('Note API', () => { expect(response?.json()).toStrictEqual({ message: 'Note not found' }); }); - test('Returns 406 when the id contains incorrect characters', async () => { + test('Returns 406 when passed id is incorrect', async () => { const expectedStatus = 406; const incorrectCharId = 'PR^B{@,&S!'; From e68457202dfd869669809fd2224958b6975a1a49 Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 18:53:49 +0300 Subject: [PATCH 35/40] Move all @todo to the end of test cases --- src/presentation/http/router/note.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index 7111795a..407cb58d 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -58,8 +58,6 @@ describe('Note API', () => { const expectedStatus = 200; const correctID = 'Pq1T9vc23Q'; - test.todo('API should not return internal id and "publicId". It should return only "id" which is public id.'); - const expectedNote = { 'id': 2, 'publicId': 'Pq1T9vc23Q', @@ -128,4 +126,6 @@ describe('Note API', () => { }); test.todo('Returns note by public id with 200 status when access is disabled, but user is creator'); + + test.todo('API should not return internal id and "publicId". It should return only "id" which is public id.'); }); \ No newline at end of file From f319bee6f87786fb0baaf55de683e22a9c8bd10f Mon Sep 17 00:00:00 2001 From: elizachi Date: Thu, 2 Nov 2023 23:35:55 +0300 Subject: [PATCH 36/40] Added 3 test cases for status 400 Removed 1 test case for 406 --- src/presentation/http/router/note.test.ts | 25 +++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index 407cb58d..c519bf1e 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -110,22 +110,31 @@ describe('Note API', () => { expect(response?.json()).toStrictEqual({ message: 'Note not found' }); }); - test('Returns 406 when passed id is incorrect', async () => { - const expectedStatus = 406; - const incorrectCharId = 'PR^B{@,&S!'; + test.each([ + { id: 'mVz3iHuez', + expectedMessage: 'params/notePublicId must NOT have fewer than 10 characters' }, + + { id: 'cR8eqF1mFf0', + expectedMessage: 'params/notePublicId must NOT have more than 10 characters' }, + + { id: '+=*&*5%&&^&-', + expectedMessage: '\'/note/+=*&*5%&&^&-\' is not a valid url component' }, + ]) + ('Returns 400 when id has incorrect characters and length', async ({ id, expectedMessage }) => { + const expectedStatus = 400; const response = await global.api?.fakeRequest({ method: 'GET', - url: `/note/${incorrectCharId}`, + url: `/note/${id}`, }); expect(response?.statusCode).toBe(expectedStatus); - expect(response?.json()).toStrictEqual({ message: 'Note not found' }); + expect(response?.json().message).toStrictEqual(expectedMessage); }); - }); - test.todo('Returns note by public id with 200 status when access is disabled, but user is creator'); + test.todo('Returns note by public id with 200 status when access is disabled, but user is creator'); - test.todo('API should not return internal id and "publicId". It should return only "id" which is public id.'); + test.todo('API should not return internal id and "publicId". It should return only "id" which is public id.'); + }); }); \ No newline at end of file From 2f5ea3021be10ab9990dd1657d16fc79cb12952b Mon Sep 17 00:00:00 2001 From: e11sy Date: Sun, 5 Nov 2023 22:26:58 +0300 Subject: [PATCH 37/40] feat(tests) : auth method added - new Globally exposed method for creating accessToken using id (global.auth) --- src/tests/utils/setup.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tests/utils/setup.ts b/src/tests/utils/setup.ts index c445c631..00fb68af 100644 --- a/src/tests/utils/setup.ts +++ b/src/tests/utils/setup.ts @@ -25,6 +25,15 @@ declare global { */ /* eslint-disable-next-line no-var */ var api: Api | undefined; + + /** + * Globally exposed method for creating accessToken using id + * Is accessed as 'global.server' in tests + * + * @param id - id for making accessToken + * @returns accessToken for authorization + */ + function auth(id: number) : string; } /** @@ -50,6 +59,9 @@ beforeAll(async () => { await insertData(orm); global.api = api; + global.auth = (id: number) => { + return domainServices.authService.signAccessToken({ id : id }); + }; }, TIMEOUT); afterAll(async () => { From 0a998e8288faf54d40785048c6976380b5e3c617 Mon Sep 17 00:00:00 2001 From: e11sy Date: Sun, 5 Nov 2023 22:35:42 +0300 Subject: [PATCH 38/40] chore(tests): readme info added - added description of global.auth method into readme --- src/tests/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tests/README.md b/src/tests/README.md index b0c5e08f..e0ce1379 100644 --- a/src/tests/README.md +++ b/src/tests/README.md @@ -8,6 +8,15 @@ File `src/tests/utils/setup.ts` does necessary preparations to make in possible ## Test data Test data lies in `src/tests/test-data/` directory. If you are missing some data for your tests, fill free to modify existing files or add new ones. Just remember to support relations between data that is usually provided by foreign key mechanism. +## Authorization in test +Use `global.auth(id)` for getting accessToken, which you can use in: +``` +headers: { + authorization: `Bearer ${accessToken}` +} +``` +in your fake rewuests + ## Writing tests Please, locate a test file near the file that it tests. Make sure test file name is the same as tested file name, but with `.test` suffix. API test file shoul follow such structure: From a149b00dc6bfd30ad9c2a0a817819fc3875c4ebc Mon Sep 17 00:00:00 2001 From: e11sy Date: Sun, 5 Nov 2023 22:45:39 +0300 Subject: [PATCH 39/40] chore(tests): rename variable - renamed variable from id to userId - typo fixed --- src/tests/README.md | 4 ++-- src/tests/utils/setup.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/README.md b/src/tests/README.md index e0ce1379..1c048b72 100644 --- a/src/tests/README.md +++ b/src/tests/README.md @@ -9,13 +9,13 @@ File `src/tests/utils/setup.ts` does necessary preparations to make in possible Test data lies in `src/tests/test-data/` directory. If you are missing some data for your tests, fill free to modify existing files or add new ones. Just remember to support relations between data that is usually provided by foreign key mechanism. ## Authorization in test -Use `global.auth(id)` for getting accessToken, which you can use in: +Use `global.auth(userId)` for getting accessToken, which you can use in: ``` headers: { authorization: `Bearer ${accessToken}` } ``` -in your fake rewuests +in your fake requests ## Writing tests Please, locate a test file near the file that it tests. Make sure test file name is the same as tested file name, but with `.test` suffix. diff --git a/src/tests/utils/setup.ts b/src/tests/utils/setup.ts index 00fb68af..c9be8c95 100644 --- a/src/tests/utils/setup.ts +++ b/src/tests/utils/setup.ts @@ -30,10 +30,10 @@ declare global { * Globally exposed method for creating accessToken using id * Is accessed as 'global.server' in tests * - * @param id - id for making accessToken + * @param userId - id of the user that will be considered the author of the request * @returns accessToken for authorization */ - function auth(id: number) : string; + function auth(userId: number) : string; } /** @@ -59,8 +59,8 @@ beforeAll(async () => { await insertData(orm); global.api = api; - global.auth = (id: number) => { - return domainServices.authService.signAccessToken({ id : id }); + global.auth = (userId: number) => { + return domainServices.authService.signAccessToken({ id : userId }); }; }, TIMEOUT); From 3df5b25738ca4bcad5936e6415f5d8961e0ed489 Mon Sep 17 00:00:00 2001 From: e11sy Date: Tue, 7 Nov 2023 03:26:57 +0300 Subject: [PATCH 40/40] chore(tests): auth method changed - authtorization with global.auth instead of fake request function - data variables renamed to camelCase - solved conflicts in test data --- src/presentation/http/router/auth.ts | 1 + src/presentation/http/router/note.test.ts | 2 +- src/presentation/http/router/noteList.test.ts | 32 +- src/tests/test-data/notes.json | 418 +++++++++--------- src/tests/utils/insert-data.ts | 2 +- 5 files changed, 221 insertions(+), 234 deletions(-) diff --git a/src/presentation/http/router/auth.ts b/src/presentation/http/router/auth.ts index d77bc231..c3775c7d 100644 --- a/src/presentation/http/router/auth.ts +++ b/src/presentation/http/router/auth.ts @@ -41,6 +41,7 @@ const AuthRouter: FastifyPluginCallback = (fastify, opts, don Reply: AuthSession | ErrorResponse; }>('/', async (request, reply) => { const { token } = request.body; + const userSession = await opts.authService.verifyRefreshToken(token); /** diff --git a/src/presentation/http/router/note.test.ts b/src/presentation/http/router/note.test.ts index c519bf1e..2ee39395 100644 --- a/src/presentation/http/router/note.test.ts +++ b/src/presentation/http/router/note.test.ts @@ -2,7 +2,7 @@ import { describe, test, expect } from 'vitest'; import notes from '@tests/test-data/notes.json'; -import noteSettings from '@tests/test-data/notes-settings.json'; +import noteSettings from '@tests/test-data/notesSettings.json'; describe('Note API', () => { describe('GET note/resolve-hostname/:hostname ', () => { diff --git a/src/presentation/http/router/noteList.test.ts b/src/presentation/http/router/noteList.test.ts index cce69aea..27d4b0e3 100644 --- a/src/presentation/http/router/noteList.test.ts +++ b/src/presentation/http/router/noteList.test.ts @@ -1,4 +1,3 @@ -import type AuthSession from '@domain/entities/authSession'; import userSessions from '@tests/test-data/userSessions.json'; import { describe, test, expect, beforeAll } from 'vitest'; @@ -8,40 +7,15 @@ import { describe, test, expect, beforeAll } from 'vitest'; */ let accessToken = ''; -/** - * Util for authorization - * - * @param refreshToken - refresh token. There should be a user session with this refresh token in database - * @todo Move this function to tests/utils - */ -async function authorize(refreshToken: string): Promise { - const response = await global.api?.fakeRequest({ - method: 'POST', - url: '/auth', - headers: { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'Content-Type': 'application/json', - }, - body : JSON.stringify({ token : refreshToken }), - }); - - const body: AuthSession = response?.body !== undefined ? JSON.parse(response?.body) : {}; - - return body.accessToken; -} describe('NoteList API', () => { beforeAll(async () => { /** - * Authorize using refresh token and POST /auth + * userId for authorization */ - const refreshToken = userSessions[0]['refresh_token']; + const userId = userSessions[0]['user_id']; - try { - accessToken = await authorize(refreshToken); - } catch (error) { - console.log('Test Authorization failed', error); - } + accessToken = global.auth(userId); }); describe('GET /notes?page', () => { diff --git a/src/tests/test-data/notes.json b/src/tests/test-data/notes.json index d93d3cbb..885ed5be 100644 --- a/src/tests/test-data/notes.json +++ b/src/tests/test-data/notes.json @@ -2,406 +2,418 @@ { "id": 1, "public_id": "note_1", - "creator_id": 1, + "creatorId": 1, "content": [], - "created_at": "2023-10-16T13:49:19.000Z", - "updated_at": "2023-10-16T13:49:19.000Z" + "createdAt": "2023-10-16T13:49:19.000Z", + "updatedAt": "2023-10-16T13:49:19.000Z" }, - { + { "id": 2, "public_id": "Pq1T9vc23Q", - "creator_id": 1 + "creatorId": 1, + "content": [], + "createdAt": "2023-10-16T13:49:19.000Z", + "updatedAt": "2023-10-16T13:49:19.000Z" }, - { - "id": 2, - "public_id": "99999902", - "creator_id": 4, + { + "id": 3, + "public_id": "73NdxFZ4k7", + "creatorId": 1, "content": [], - "created_at": "2023-10-16T13:49:2.000Z", - "updated_at": "2023-10-16T13:49:2.000Z" + "createdAt": "2023-10-16T13:49:19.000Z", + "updatedAt": "2023-10-16T13:49:19.000Z" }, { - "id": 3, + "id": 4, "public_id": "99999903", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:3.000Z", - "updated_at": "2023-10-16T13:49:3.000Z" + "createdAt": "2023-10-16T13:49:3.000Z", + "updatedAt": "2023-10-16T13:49:3.000Z" }, { - "id": 4, + "id": 5, "public_id": "99999904", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:4.000Z", - "updated_at": "2023-10-16T13:49:4.000Z" + "createdAt": "2023-10-16T13:49:4.000Z", + "updatedAt": "2023-10-16T13:49:4.000Z" }, { - "id": 5, + "id": 6, "public_id": "99999905", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:5.000Z", - "updated_at": "2023-10-16T13:49:5.000Z" + "createdAt": "2023-10-16T13:49:5.000Z", + "updatedAt": "2023-10-16T13:49:5.000Z" }, { - "id": 6, + "id": 7, "public_id": "99999906", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:6.000Z", - "updated_at": "2023-10-16T13:49:6.000Z" + "createdAt": "2023-10-16T13:49:6.000Z", + "updatedAt": "2023-10-16T13:49:6.000Z" }, { - "id": 7, + "id": 8, "public_id": "99999907", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:7.000Z", - "updated_at": "2023-10-16T13:49:7.000Z" + "createdAt": "2023-10-16T13:49:7.000Z", + "updatedAt": "2023-10-16T13:49:7.000Z" }, { - "id": 8, + "id": 9, "public_id": "99999908", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:8.000Z", - "updated_at": "2023-10-16T13:49:8.000Z" + "createdAt": "2023-10-16T13:49:8.000Z", + "updatedAt": "2023-10-16T13:49:8.000Z" }, { - "id": 9, + "id": 10, "public_id": "99999909", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:9.000Z", - "updated_at": "2023-10-16T13:49:9.000Z" + "createdAt": "2023-10-16T13:49:9.000Z", + "updatedAt": "2023-10-16T13:49:9.000Z" }, { - "id": 10, + "id": 11, "public_id": "99999910", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:10.000Z", - "updated_at": "2023-10-16T13:49:10.000Z" + "createdAt": "2023-10-16T13:49:10.000Z", + "updatedAt": "2023-10-16T13:49:10.000Z" }, { - "id": 11, + "id": 12, "public_id": "99999911", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:11.000Z", - "updated_at": "2023-10-16T13:49:11.000Z" + "createdAt": "2023-10-16T13:49:11.000Z", + "updatedAt": "2023-10-16T13:49:11.000Z" }, { - "id": 12, + "id": 13, "public_id": "99999912", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:12.000Z", - "updated_at": "2023-10-16T13:49:12.000Z" + "createdAt": "2023-10-16T13:49:12.000Z", + "updatedAt": "2023-10-16T13:49:12.000Z" }, { - "id": 13, + "id": 14, "public_id": "99999913", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:13.000Z", - "updated_at": "2023-10-16T13:49:13.000Z" + "createdAt": "2023-10-16T13:49:13.000Z", + "updatedAt": "2023-10-16T13:49:13.000Z" }, { - "id": 14, + "id": 15, "public_id": "99999914", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:14.000Z", - "updated_at": "2023-10-16T13:49:14.000Z" + "createdAt": "2023-10-16T13:49:14.000Z", + "updatedAt": "2023-10-16T13:49:14.000Z" }, { - "id": 15, + "id": 16, "public_id": "99999915", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:15.000Z", - "updated_at": "2023-10-16T13:49:15.000Z" + "createdAt": "2023-10-16T13:49:15.000Z", + "updatedAt": "2023-10-16T13:49:15.000Z" }, { - "id": 16, + "id": 17, "public_id": "99999916", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:16.000Z", - "updated_at": "2023-10-16T13:49:16.000Z" + "createdAt": "2023-10-16T13:49:16.000Z", + "updatedAt": "2023-10-16T13:49:16.000Z" }, { - "id": 17, + "id": 18, "public_id": "99999917", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:17.000Z", - "updated_at": "2023-10-16T13:49:17.000Z" + "createdAt": "2023-10-16T13:49:17.000Z", + "updatedAt": "2023-10-16T13:49:17.000Z" }, { - "id": 18, + "id": 19, "public_id": "99999918", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:18.000Z", - "updated_at": "2023-10-16T13:49:18.000Z" + "createdAt": "2023-10-16T13:49:18.000Z", + "updatedAt": "2023-10-16T13:49:18.000Z" }, { - "id": 19, + "id": 20, "public_id": "99999919", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:19.000Z", - "updated_at": "2023-10-16T13:49:19.000Z" + "createdAt": "2023-10-16T13:49:19.000Z", + "updatedAt": "2023-10-16T13:49:19.000Z" }, { - "id": 20, + "id": 21, "public_id": "99999920", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:20.000Z", - "updated_at": "2023-10-16T13:49:20.000Z" + "createdAt": "2023-10-16T13:49:20.000Z", + "updatedAt": "2023-10-16T13:49:20.000Z" }, { - "id": 21, + "id": 22, "public_id": "99999921", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:21.000Z", - "updated_at": "2023-10-16T13:49:21.000Z" + "createdAt": "2023-10-16T13:49:21.000Z", + "updatedAt": "2023-10-16T13:49:21.000Z" }, { - "id": 22, + "id": 23, "public_id": "99999922", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:22.000Z", - "updated_at": "2023-10-16T13:49:22.000Z" + "createdAt": "2023-10-16T13:49:22.000Z", + "updatedAt": "2023-10-16T13:49:22.000Z" }, { - "id": 23, + "id": 24, "public_id": "99999923", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:23.000Z", - "updated_at": "2023-10-16T13:49:23.000Z" + "createdAt": "2023-10-16T13:49:23.000Z", + "updatedAt": "2023-10-16T13:49:23.000Z" }, { - "id": 24, + "id": 25, "public_id": "99999924", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:24.000Z", - "updated_at": "2023-10-16T13:49:24.000Z" + "createdAt": "2023-10-16T13:49:24.000Z", + "updatedAt": "2023-10-16T13:49:24.000Z" }, { - "id": 25, + "id": 26, "public_id": "99999925", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:25.000Z", - "updated_at": "2023-10-16T13:49:25.000Z" + "createdAt": "2023-10-16T13:49:25.000Z", + "updatedAt": "2023-10-16T13:49:25.000Z" }, { - "id": 26, + "id": 27, "public_id": "99999926", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:26.000Z", - "updated_at": "2023-10-16T13:49:26.000Z" + "createdAt": "2023-10-16T13:49:26.000Z", + "updatedAt": "2023-10-16T13:49:26.000Z" }, { - "id": 27, + "id": 28, "public_id": "99999927", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:27.000Z", - "updated_at": "2023-10-16T13:49:27.000Z" + "createdAt": "2023-10-16T13:49:27.000Z", + "updatedAt": "2023-10-16T13:49:27.000Z" }, { - "id": 28, + "id": 29, "public_id": "99999928", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:28.000Z", - "updated_at": "2023-10-16T13:49:28.000Z" + "createdAt": "2023-10-16T13:49:28.000Z", + "updatedAt": "2023-10-16T13:49:28.000Z" }, { - "id": 29, + "id": 30, "public_id": "99999929", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:29.000Z", - "updated_at": "2023-10-16T13:49:29.000Z" + "createdAt": "2023-10-16T13:49:29.000Z", + "updatedAt": "2023-10-16T13:49:29.000Z" }, { - "id": 30, + "id": 31, "public_id": "99999930", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:30.000Z", - "updated_at": "2023-10-16T13:49:30.000Z" + "createdAt": "2023-10-16T13:49:30.000Z", + "updatedAt": "2023-10-16T13:49:30.000Z" }, { - "id": 31, + "id": 32, "public_id": "99999931", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:31.000Z", - "updated_at": "2023-10-16T13:49:31.000Z" + "createdAt": "2023-10-16T13:49:31.000Z", + "updatedAt": "2023-10-16T13:49:31.000Z" }, { - "id": 32, + "id": 33, "public_id": "99999932", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:32.000Z", - "updated_at": "2023-10-16T13:49:32.000Z" + "createdAt": "2023-10-16T13:49:32.000Z", + "updatedAt": "2023-10-16T13:49:32.000Z" }, { - "id": 33, + "id": 34, "public_id": "99999933", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:33.000Z", - "updated_at": "2023-10-16T13:49:33.000Z" + "createdAt": "2023-10-16T13:49:33.000Z", + "updatedAt": "2023-10-16T13:49:33.000Z" }, { - "id": 34, + "id": 35, "public_id": "99999934", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:34.000Z", - "updated_at": "2023-10-16T13:49:34.000Z" + "createdAt": "2023-10-16T13:49:34.000Z", + "updatedAt": "2023-10-16T13:49:34.000Z" }, { - "id": 35, + "id": 36, "public_id": "99999935", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:35.000Z", - "updated_at": "2023-10-16T13:49:35.000Z" + "createdAt": "2023-10-16T13:49:35.000Z", + "updatedAt": "2023-10-16T13:49:35.000Z" }, { - "id": 36, + "id": 37, "public_id": "99999936", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:36.000Z", - "updated_at": "2023-10-16T13:49:36.000Z" + "createdAt": "2023-10-16T13:49:36.000Z", + "updatedAt": "2023-10-16T13:49:36.000Z" }, { - "id": 37, + "id": 38, "public_id": "99999937", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:37.000Z", - "updated_at": "2023-10-16T13:49:37.000Z" + "createdAt": "2023-10-16T13:49:37.000Z", + "updatedAt": "2023-10-16T13:49:37.000Z" }, { - "id": 38, + "id": 39, "public_id": "99999938", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:38.000Z", - "updated_at": "2023-10-16T13:49:38.000Z" + "createdAt": "2023-10-16T13:49:38.000Z", + "updatedAt": "2023-10-16T13:49:38.000Z" }, { - "id": 39, + "id": 40 + , "public_id": "99999939", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:39.000Z", - "updated_at": "2023-10-16T13:49:39.000Z" + "createdAt": "2023-10-16T13:49:39.000Z", + "updatedAt": "2023-10-16T13:49:39.000Z" }, { - "id": 40, + "id": 41, "public_id": "99999940", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:40.000Z", - "updated_at": "2023-10-16T13:49:40.000Z" + "createdAt": "2023-10-16T13:49:40.000Z", + "updatedAt": "2023-10-16T13:49:40.000Z" }, { - "id": 41, + "id": 42, "public_id": "99999941", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:41.000Z", - "updated_at": "2023-10-16T13:49:41.000Z" + "createdAt": "2023-10-16T13:49:41.000Z", + "updatedAt": "2023-10-16T13:49:41.000Z" }, { - "id": 42, + "id": 43, "public_id": "99999942", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:42.000Z", - "updated_at": "2023-10-16T13:49:42.000Z" + "createdAt": "2023-10-16T13:49:42.000Z", + "updatedAt": "2023-10-16T13:49:42.000Z" }, { - "id": 43, + "id": 44, "public_id": "99999943", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:43.000Z", - "updated_at": "2023-10-16T13:49:43.000Z" + "createdAt": "2023-10-16T13:49:43.000Z", + "updatedAt": "2023-10-16T13:49:43.000Z" }, { - "id": 44, + "id": 45, "public_id": "99999944", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:44.000Z", - "updated_at": "2023-10-16T13:49:44.000Z" + "createdAt": "2023-10-16T13:49:44.000Z", + "updatedAt": "2023-10-16T13:49:44.000Z" }, { - "id": 45, + "id": 46, "public_id": "99999945", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:45.000Z", - "updated_at": "2023-10-16T13:49:45.000Z" + "createdAt": "2023-10-16T13:49:45.000Z", + "updatedAt": "2023-10-16T13:49:45.000Z" }, { - "id": 46, + "id": 47, "public_id": "99999946", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:46.000Z", - "updated_at": "2023-10-16T13:49:46.000Z" + "createdAt": "2023-10-16T13:49:46.000Z", + "updatedAt": "2023-10-16T13:49:46.000Z" }, { - "id": 47, + "id": 48, "public_id": "99999947", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:47.000Z", - "updated_at": "2023-10-16T13:49:47.000Z" + "createdAt": "2023-10-16T13:49:47.000Z", + "updatedAt": "2023-10-16T13:49:47.000Z" }, { - "id": 48, + "id": 49, "public_id": "99999948", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:48.000Z", - "updated_at": "2023-10-16T13:49:48.000Z" + "createdAt": "2023-10-16T13:49:48.000Z", + "updatedAt": "2023-10-16T13:49:48.000Z" }, { - "id": 49, + "id": 50, "public_id": "99999949", - "creator_id": 4, + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:49.000Z", - "updated_at": "2023-10-16T13:49:49.000Z" + "createdAt": "2023-10-16T13:49:49.000Z", + "updatedAt": "2023-10-16T13:49:49.000Z" }, { - "id": 50, + "id": 51, "public_id": "99999950", - "creator_id": 4, + "creatorId": 4, + "content": [], + "createdAt": "2023-10-16T13:49:50.000Z", + "updatedAt": "2023-10-16T13:49:50.000Z" + }, + { + "id": 52, + "public_id": "99999902", + "creatorId": 4, "content": [], - "created_at": "2023-10-16T13:49:50.000Z", - "updated_at": "2023-10-16T13:49:50.000Z" + "createdAt": "2023-10-16T13:49:2.000Z", + "updatedAt": "2023-10-16T13:49:2.000Z" } ] \ No newline at end of file diff --git a/src/tests/utils/insert-data.ts b/src/tests/utils/insert-data.ts index 76140a7c..fcb8aae2 100644 --- a/src/tests/utils/insert-data.ts +++ b/src/tests/utils/insert-data.ts @@ -32,7 +32,7 @@ async function insertUserSessions(db: SequelizeOrm): Promise { */ async function insertNotes(db: SequelizeOrm): Promise { for (const note of notes) { - await db.connection.query(`INSERT INTO public.notes (id, "public_id", "creator_id", "created_at", "updated_at") VALUES (${note.id}, '${note.public_id}', '${note.creator_id}', '${note.created_at}', '${note.updated_at}')`); + await db.connection.query(`INSERT INTO public.notes (id, "public_id", "creator_id", "created_at", "updated_at") VALUES (${note.id}, '${note.public_id}', '${note.creatorId}', '${note.createdAt}', '${note.updatedAt}')`); } }