From 16855a14936cbee30ab009e48cfc1c7966fe0802 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 14 Oct 2024 07:10:53 +0200 Subject: [PATCH 1/7] fix: organization sprint update activity log --- .../organization-sprint.service.ts | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/packages/core/src/organization-sprint/organization-sprint.service.ts b/packages/core/src/organization-sprint/organization-sprint.service.ts index 592be390537..8c967930a96 100644 --- a/packages/core/src/organization-sprint/organization-sprint.service.ts +++ b/packages/core/src/organization-sprint/organization-sprint.service.ts @@ -5,7 +5,6 @@ import { EntityEnum, ActorTypeEnum, FavoriteEntityEnum, - IActivityLogUpdatedValues, ID, IEmployee, IOrganizationSprint, @@ -20,7 +19,7 @@ import { OrganizationSprintEmployee } from '../core/entities/internal'; import { FavoriteService } from '../core/decorators'; // import { prepareSQLQuery as p } from './../database/database.helper'; import { ActivityLogEvent } from '../activity-log/events'; -import { generateActivityLogDescription } from '../activity-log/activity-log.helper'; +import { activityLogUpdatedFieldsAndValues, generateActivityLogDescription } from '../activity-log/activity-log.helper'; import { RoleService } from '../role/role.service'; import { EmployeeService } from '../employee/employee.service'; import { OrganizationSprint } from './organization-sprint.entity'; @@ -201,20 +200,10 @@ export class OrganizationSprintService extends TenantAwareCrudService Date: Mon, 14 Oct 2024 07:37:04 +0200 Subject: [PATCH 2/7] feat: task view activity log --- packages/contracts/src/base-entity.model.ts | 1 + .../handlers/task-view-update.handler.ts | 5 +- packages/core/src/tasks/views/view.service.ts | 118 +++++++++++++++++- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/packages/contracts/src/base-entity.model.ts b/packages/contracts/src/base-entity.model.ts index a8e17a638c6..c3edc565912 100644 --- a/packages/contracts/src/base-entity.model.ts +++ b/packages/contracts/src/base-entity.model.ts @@ -82,5 +82,6 @@ export enum EntityEnum { OrganizationProjectModule = 'OrganizationProjectModule', OrganizationSprint = 'OrganizationSprint', Task = 'Task', + TaskView = 'TaskView', User = 'User' } diff --git a/packages/core/src/tasks/views/commands/handlers/task-view-update.handler.ts b/packages/core/src/tasks/views/commands/handlers/task-view-update.handler.ts index 1c1e04cb0b5..ae6a7386e3a 100644 --- a/packages/core/src/tasks/views/commands/handlers/task-view-update.handler.ts +++ b/packages/core/src/tasks/views/commands/handlers/task-view-update.handler.ts @@ -10,9 +10,6 @@ export class TaskViewUpdateHandler implements ICommandHandler { const { id, input } = command; - return await this.taskViewService.create({ - ...input, - id - }); + return await this.taskViewService.update(id, input); } } diff --git a/packages/core/src/tasks/views/view.service.ts b/packages/core/src/tasks/views/view.service.ts index 54e2c089665..606d6e51d78 100644 --- a/packages/core/src/tasks/views/view.service.ts +++ b/packages/core/src/tasks/views/view.service.ts @@ -1,8 +1,24 @@ -import { Injectable } from '@nestjs/common'; +import { EventBus } from '@nestjs/cqrs'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { FavoriteEntityEnum } from '@gauzy/contracts'; +import { + ActionTypeEnum, + ActorTypeEnum, + EntityEnum, + FavoriteEntityEnum, + ID, + ITaskView, + ITaskViewCreateInput, + ITaskViewUpdateInput +} from '@gauzy/contracts'; import { FavoriteService } from '../../core/decorators'; import { TenantAwareCrudService } from '../../core/crud'; +import { RequestContext } from '../../core/context'; +import { ActivityLogEvent } from '../../activity-log/events'; +import { + activityLogUpdatedFieldsAndValues, + generateActivityLogDescription +} from '../../activity-log/activity-log.helper'; import { TaskView } from './view.entity'; import { TypeOrmTaskViewRepository } from './repository/type-orm-task-view.repository'; import { MikroOrmTaskViewRepository } from './repository/mikro-orm-task-view.repository'; @@ -14,8 +30,104 @@ export class TaskViewService extends TenantAwareCrudService { @InjectRepository(TaskView) typeOrmTaskViewRepository: TypeOrmTaskViewRepository, - mikroOrmTaskViewRepository: MikroOrmTaskViewRepository + mikroOrmTaskViewRepository: MikroOrmTaskViewRepository, + + private readonly _eventBus: EventBus ) { super(typeOrmTaskViewRepository, mikroOrmTaskViewRepository); } + + /** + * @description Creates a Task View based on provided input + * @param {ITaskViewCreateInput} entity - Input data for creating the task view + * @returns A promise resolving to the created Task View + * @throws BadRequestException if there is an error in the creation process. + * @memberof TaskViewService + */ + async create(entity: ITaskViewCreateInput): Promise { + const tenantId = RequestContext.currentTenantId() || entity.tenantId; + const { organizationId } = entity; + try { + const view = await super.create({ ...entity, tenantId }); + + // Generate the activity log description. + const description = generateActivityLogDescription(ActionTypeEnum.Created, EntityEnum.TaskView, view.name); + + // Emit an event to log the activity + this._eventBus.publish( + new ActivityLogEvent({ + entity: EntityEnum.TaskView, + entityId: view.id, + action: ActionTypeEnum.Created, + actorType: ActorTypeEnum.User, + description, + data: view, + organizationId, + tenantId + }) + ); + + return view; + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to create view : ${error.message}`, HttpStatus.BAD_REQUEST); + } + } + + /** + * @description Update a Task View + * @author GloireMutaliko + * @param {ID} id - The ID of the Task View to be updated + * @param {ITaskViewUpdateInput} input - The updated information for the Task View + * @throws BadRequestException if there's an error during the update process. + * @returns {Promise} A Promise resolving to the updated Task View + * @memberof TaskViewService + */ + async update(id: ID, input: ITaskViewUpdateInput): Promise { + const tenantId = RequestContext.currentTenantId() || input.tenantId; + + try { + const updatedTaskView = await super.create({ + ...input, + tenantId, + id + }); + + // Generate the activity log description. + const description = generateActivityLogDescription( + ActionTypeEnum.Updated, + EntityEnum.TaskView, + updatedTaskView.name + ); + + // Compare values before and after update then add updates to fields + const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues( + updatedTaskView, + input + ); + + // Emit event to log activity + this._eventBus.publish( + new ActivityLogEvent({ + entity: EntityEnum.TaskView, + entityId: updatedTaskView.id, + action: ActionTypeEnum.Updated, + actorType: ActorTypeEnum.User, + description, + updatedFields, + updatedValues, + previousValues, + data: updatedTaskView, + organizationId: updatedTaskView.organizationId, + tenantId + }) + ); + + // return updated view + return updatedTaskView; + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to update view: ${error.message}`, HttpStatus.BAD_REQUEST); + } + } } From b1f908281b2e8fc17686f473a70e547d090c9a9b Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 14 Oct 2024 07:49:47 +0200 Subject: [PATCH 3/7] feat: resource link activity log --- packages/contracts/src/base-entity.model.ts | 1 + .../resource-link/resource-link.service.ts | 76 ++++++++++++++++++- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/packages/contracts/src/base-entity.model.ts b/packages/contracts/src/base-entity.model.ts index c3edc565912..2de06ddb3bd 100644 --- a/packages/contracts/src/base-entity.model.ts +++ b/packages/contracts/src/base-entity.model.ts @@ -81,6 +81,7 @@ export enum EntityEnum { OrganizationTeam = 'OrganizationTeam', OrganizationProjectModule = 'OrganizationProjectModule', OrganizationSprint = 'OrganizationSprint', + ResourceLink = 'ResourceLink', Task = 'Task', TaskView = 'TaskView', User = 'User' diff --git a/packages/core/src/resource-link/resource-link.service.ts b/packages/core/src/resource-link/resource-link.service.ts index f748cf542d4..a9d65a0231c 100644 --- a/packages/core/src/resource-link/resource-link.service.ts +++ b/packages/core/src/resource-link/resource-link.service.ts @@ -1,8 +1,19 @@ +import { EventBus } from '@nestjs/cqrs'; import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { UpdateResult } from 'typeorm'; +import { + IResourceLink, + IResourceLinkCreateInput, + IResourceLinkUpdateInput, + ID, + ActionTypeEnum, + EntityEnum, + ActorTypeEnum +} from '@gauzy/contracts'; import { TenantAwareCrudService } from './../core/crud'; import { RequestContext } from '../core/context'; -import { IResourceLink, IResourceLinkCreateInput, IResourceLinkUpdateInput, ID } from '@gauzy/contracts'; +import { ActivityLogEvent } from '../activity-log/events'; +import { activityLogUpdatedFieldsAndValues, generateActivityLogDescription } from '../activity-log/activity-log.helper'; import { UserService } from '../user/user.service'; import { ResourceLink } from './resource-link.entity'; import { TypeOrmResourceLinkRepository } from './repository/type-orm-resource-link.repository'; @@ -13,7 +24,8 @@ export class ResourceLinkService extends TenantAwareCrudService { constructor( readonly typeOrmResourceLinkRepository: TypeOrmResourceLinkRepository, readonly mikroOrmResourceLinkRepository: MikroOrmResourceLinkRepository, - private readonly userService: UserService + private readonly userService: UserService, + private readonly _eventBus: EventBus ) { super(typeOrmResourceLinkRepository, mikroOrmResourceLinkRepository); } @@ -37,11 +49,34 @@ export class ResourceLinkService extends TenantAwareCrudService { } // return created resource link - return await super.create({ + const resourceLink = await super.create({ ...entity, tenantId, creatorId: user.id }); + + // Generate the activity log description. + const description = generateActivityLogDescription( + ActionTypeEnum.Created, + EntityEnum.ResourceLink, + `${resourceLink.title} for ${resourceLink.entity}` + ); + + // Emit an event to log the activity + this._eventBus.publish( + new ActivityLogEvent({ + entity: EntityEnum.ResourceLink, + entityId: resourceLink.id, + action: ActionTypeEnum.Created, + actorType: ActorTypeEnum.User, + description, + data: resourceLink, + organizationId: resourceLink.organizationId, + tenantId + }) + ); + + return resourceLink; } catch (error) { console.log(error); // Debug Logging throw new BadRequestException('Resource Link creation failed', error); @@ -62,10 +97,43 @@ export class ResourceLinkService extends TenantAwareCrudService { throw new BadRequestException('Resource Link not found'); } - return await super.create({ + const updatedResourceLink = await super.create({ ...input, id }); + + // Generate the activity log description. + const description = generateActivityLogDescription( + ActionTypeEnum.Updated, + EntityEnum.ResourceLink, + `${resourceLink.title} for ${resourceLink.entity}` + ); + + // Compare values before and after update then add updates to fields + const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues( + updatedResourceLink, + input + ); + + // Emit event to log activity + this._eventBus.publish( + new ActivityLogEvent({ + entity: EntityEnum.ResourceLink, + entityId: updatedResourceLink.id, + action: ActionTypeEnum.Updated, + actorType: ActorTypeEnum.User, + description, + updatedFields, + updatedValues, + previousValues, + data: updatedResourceLink, + organizationId: updatedResourceLink.organizationId, + tenantId: updatedResourceLink.tenantId + }) + ); + + // return updated Resource Link + return updatedResourceLink; } catch (error) { console.log(error); // Debug Logging throw new BadRequestException('Resource Link update failed', error); From 3e74508a4e58c1ee4cd24dd2083c36c84a2b5080 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 14 Oct 2024 08:00:55 +0200 Subject: [PATCH 4/7] fix: removed unecessary js-docs --- packages/core/src/tasks/views/view.service.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/tasks/views/view.service.ts b/packages/core/src/tasks/views/view.service.ts index 606d6e51d78..4c07a18d17f 100644 --- a/packages/core/src/tasks/views/view.service.ts +++ b/packages/core/src/tasks/views/view.service.ts @@ -76,7 +76,6 @@ export class TaskViewService extends TenantAwareCrudService { /** * @description Update a Task View - * @author GloireMutaliko * @param {ID} id - The ID of the Task View to be updated * @param {ITaskViewUpdateInput} input - The updated information for the Task View * @throws BadRequestException if there's an error during the update process. From 34a4edd1e83f11e3926be1c12c9a3a75dabaf5c5 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 14 Oct 2024 10:22:26 +0200 Subject: [PATCH 5/7] fix: use global base entity enum --- packages/contracts/src/activity-log.model.ts | 10 ++++-- packages/contracts/src/base-entity.model.ts | 2 +- packages/contracts/src/comment.model.ts | 24 ++------------ packages/contracts/src/favorite.model.ts | 4 +-- packages/contracts/src/index.ts | 2 +- packages/contracts/src/resource-link.model.ts | 4 +-- .../src/activity-log/activity-log.entity.ts | 8 ++--- .../src/activity-log/activity-log.helper.ts | 8 +++-- .../activity-log/activity-log.subscriber.ts | 32 +++++++++++++++---- .../activity-log/dto/get-activity-logs.dto.ts | 8 ++--- packages/core/src/comment/comment.entity.ts | 8 ++--- .../src/core/decorators/is-favoritable.ts | 4 +-- packages/core/src/favorite/favorite.entity.ts | 8 ++--- .../core/src/favorite/favorite.service.ts | 4 +-- .../global-favorite-service.service.ts | 10 +++--- .../organization-project-module.service.ts | 10 +++--- .../organization-project.service.ts | 8 ++--- .../organization-sprint.service.ts | 12 +++---- .../organization-team.service.ts | 4 +-- .../src/resource-link/resource-link.entity.ts | 8 ++--- .../resource-link/resource-link.service.ts | 10 +++--- .../commands/handlers/task-create.handler.ts | 8 ++--- packages/core/src/tasks/task.service.ts | 6 ++-- packages/core/src/tasks/views/view.service.ts | 16 ++++++---- 24 files changed, 117 insertions(+), 101 deletions(-) diff --git a/packages/contracts/src/activity-log.model.ts b/packages/contracts/src/activity-log.model.ts index 96a6ae41633..008d456a979 100644 --- a/packages/contracts/src/activity-log.model.ts +++ b/packages/contracts/src/activity-log.model.ts @@ -1,11 +1,17 @@ -import { ActorTypeEnum, EntityEnum, IBasePerTenantAndOrganizationEntityModel, ID, JsonData } from './base-entity.model'; +import { + ActorTypeEnum, + BaseEntityEnum, + IBasePerTenantAndOrganizationEntityModel, + ID, + JsonData +} from './base-entity.model'; import { IUser } from './user.model'; /** * Interface representing an activity log entry. */ export interface IActivityLog extends IBasePerTenantAndOrganizationEntityModel { - entity: EntityEnum; // Entity / Table name concerned by activity log + entity: BaseEntityEnum; // Entity / Table name concerned by activity log entityId: ID; // The ID of the element we are interacting with (a task, an organization, an employee, ...) action: ActionTypeEnum; actorType?: ActorTypeEnum; diff --git a/packages/contracts/src/base-entity.model.ts b/packages/contracts/src/base-entity.model.ts index 5bf2854ff37..c7cc7080d32 100644 --- a/packages/contracts/src/base-entity.model.ts +++ b/packages/contracts/src/base-entity.model.ts @@ -65,7 +65,7 @@ export enum ActorTypeEnum { User = 1 // User performed the action } -export enum EntityEnum { +export enum BaseEntityEnum { Candidate = 'Candidate', Contact = 'Contact', Currency = 'Currency', diff --git a/packages/contracts/src/comment.model.ts b/packages/contracts/src/comment.model.ts index 3e5308fed6e..63d42b4b0d3 100644 --- a/packages/contracts/src/comment.model.ts +++ b/packages/contracts/src/comment.model.ts @@ -1,10 +1,10 @@ -import { ActorTypeEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; +import { ActorTypeEnum, BaseEntityEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; import { IUser } from './user.model'; import { IEmployee } from './employee.model'; import { IOrganizationTeam } from './organization-team.model'; export interface IComment extends IBasePerTenantAndOrganizationEntityModel { - entity: CommentEntityEnum; + entity: BaseEntityEnum; entityId: ID; // Indicate the ID of entity record comment related to comment: string; creator?: IUser; @@ -22,27 +22,9 @@ export interface IComment extends IBasePerTenantAndOrganizationEntityModel { replies?: IComment[]; } -export enum CommentEntityEnum { - Contact = 'Contact', - Employee = 'Employee', - Expense = 'Expense', - DailyPlan = 'DailyPlan', - Invoice = 'Invoice', - Income = 'Income', - Organization = 'Organization', - OrganizationContact = 'OrganizationContact', - OrganizationDepartment = 'OrganizationDepartment', - OrganizationDocument = 'OrganizationDocument', - OrganizationProject = 'OrganizationProject', - OrganizationTeam = 'OrganizationTeam', - OrganizationProjectModule = 'OrganizationProjectModule', - OrganizationSprint = 'OrganizationSprint', - Task = 'Task' -} - export interface ICommentCreateInput { comment: string; - entity: CommentEntityEnum; + entity: BaseEntityEnum; entityId: ID; parentId?: ID; members?: IEmployee[]; diff --git a/packages/contracts/src/favorite.model.ts b/packages/contracts/src/favorite.model.ts index 7820c7d3f6b..fc4264f6527 100644 --- a/packages/contracts/src/favorite.model.ts +++ b/packages/contracts/src/favorite.model.ts @@ -1,8 +1,8 @@ -import { EntityEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; +import { BaseEntityEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; import { IEmployeeEntityInput } from './employee.model'; export interface IFavorite extends IBasePerTenantAndOrganizationEntityModel, IEmployeeEntityInput { - entity: EntityEnum; + entity: BaseEntityEnum; entityId: ID; // Indicate the ID of entity record marked as favorite } diff --git a/packages/contracts/src/index.ts b/packages/contracts/src/index.ts index b689e79eeac..bee9550ceac 100644 --- a/packages/contracts/src/index.ts +++ b/packages/contracts/src/index.ts @@ -146,7 +146,7 @@ export { IBaseRelationsEntityModel, ActorTypeEnum, JsonData, - EntityEnum + BaseEntityEnum } from './base-entity.model'; export * from './proxy.model'; diff --git a/packages/contracts/src/resource-link.model.ts b/packages/contracts/src/resource-link.model.ts index 39e1b8b1c20..1fed06631f9 100644 --- a/packages/contracts/src/resource-link.model.ts +++ b/packages/contracts/src/resource-link.model.ts @@ -1,9 +1,9 @@ import { IURLMetaData } from './timesheet.model'; -import { EntityEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; +import { BaseEntityEnum, IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; import { IUser } from './user.model'; export interface IResourceLink extends IBasePerTenantAndOrganizationEntityModel { - entity: EntityEnum; + entity: BaseEntityEnum; entityId: ID; title: string; url: string; diff --git a/packages/core/src/activity-log/activity-log.entity.ts b/packages/core/src/activity-log/activity-log.entity.ts index a265db2bb53..1972bd25a58 100644 --- a/packages/core/src/activity-log/activity-log.entity.ts +++ b/packages/core/src/activity-log/activity-log.entity.ts @@ -3,7 +3,7 @@ import { EntityRepositoryType } from '@mikro-orm/core'; import { JoinColumn, RelationId } from 'typeorm'; import { IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator'; import { isMySQL, isPostgres } from '@gauzy/config'; -import { EntityEnum, ActionTypeEnum, ActorTypeEnum, IActivityLog, ID, IUser, JsonData } from '@gauzy/contracts'; +import { BaseEntityEnum, ActionTypeEnum, ActorTypeEnum, IActivityLog, ID, IUser, JsonData } from '@gauzy/contracts'; import { TenantOrganizationBaseEntity, User } from '../core/entities/internal'; import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToOne } from '../core/decorators/entity'; import { MikroOrmActivityLogRepository } from './repository/mikro-orm-activity-log.repository'; @@ -12,12 +12,12 @@ import { MikroOrmActivityLogRepository } from './repository/mikro-orm-activity-l export class ActivityLog extends TenantOrganizationBaseEntity implements IActivityLog { [EntityRepositoryType]?: MikroOrmActivityLogRepository; - @ApiProperty({ enum: EntityEnum }) + @ApiProperty({ enum: BaseEntityEnum }) @IsNotEmpty() - @IsEnum(EntityEnum) + @IsEnum(BaseEntityEnum) @ColumnIndex() @MultiORMColumn() - entity: EntityEnum; + entity: BaseEntityEnum; @ApiProperty({ type: () => String }) @IsNotEmpty() diff --git a/packages/core/src/activity-log/activity-log.helper.ts b/packages/core/src/activity-log/activity-log.helper.ts index 62e01691644..d4825c47c6d 100644 --- a/packages/core/src/activity-log/activity-log.helper.ts +++ b/packages/core/src/activity-log/activity-log.helper.ts @@ -1,4 +1,4 @@ -import { ActionTypeEnum, EntityEnum, IActivityLogUpdatedValues } from '@gauzy/contracts'; +import { ActionTypeEnum, BaseEntityEnum, IActivityLogUpdatedValues } from '@gauzy/contracts'; const ActivityTemplates = { [ActionTypeEnum.Created]: `{action} a new {entity} called "{entityName}"`, @@ -13,7 +13,11 @@ const ActivityTemplates = { * @param entityName - The name of the specific entity instance. * @returns A formatted description string. */ -export function generateActivityLogDescription(action: ActionTypeEnum, entity: EntityEnum, entityName: string): string { +export function generateActivityLogDescription( + action: ActionTypeEnum, + entity: BaseEntityEnum, + entityName: string +): string { // Get the template corresponding to the action const template = ActivityTemplates[action] || '{action} {entity} "{entityName}"'; diff --git a/packages/core/src/activity-log/activity-log.subscriber.ts b/packages/core/src/activity-log/activity-log.subscriber.ts index 512f9ec02dd..aa658ac073f 100644 --- a/packages/core/src/activity-log/activity-log.subscriber.ts +++ b/packages/core/src/activity-log/activity-log.subscriber.ts @@ -23,9 +23,19 @@ export class ActivityLogSubscriber extends BaseEntityEventSubscriber { try { - // Check if the database is SQLite and the entity's data is an object - if ((isSqlite() || isBetterSqlite3()) && typeof entity.data === 'object') { - entity.data = JSON.stringify(entity.data); + // Check if the database is SQLite + if (isSqlite() || isBetterSqlite3()) { + // Serialize the `data` field if it's an object + if (typeof entity.data === 'object') { + entity.data = JSON.stringify(entity.data); + } + + // Serialize `updatedValues`, `previousValues`, `updatedEntities`, `previousEntities` if they are arrays or objects + ['updatedValues', 'previousValues', 'updatedEntities', 'previousEntities'].forEach((field) => { + if (Array.isArray(entity[field]) || typeof entity[field] === 'object') { + entity[field] = JSON.stringify(entity[field]); + } + }); } } catch (error) { // Log the error and reset the data to an empty object if JSON parsing fails @@ -66,9 +76,19 @@ export class ActivityLogSubscriber extends BaseEntityEventSubscriber { try { - // Check if the database is SQLite and if `data` is a non-null string - if ((isSqlite() || isBetterSqlite3()) && entity.data && typeof entity.data === 'string') { - entity.data = JSON.parse(entity.data); + // Check if the database is SQLite + if (isSqlite() || isBetterSqlite3()) { + // Parse the `data` field if it's a string + if (entity.data && typeof entity.data === 'string') { + entity.data = JSON.parse(entity.data); + } + + // Parse `updatedValues`, `previousValues`, `updatedEntities`, `previousEntities` if they are strings + ['updatedValues', 'previousValues', 'updatedEntities', 'previousEntities'].forEach((field) => { + if (entity[field] && typeof entity[field] === 'string') { + entity[field] = JSON.parse(entity[field]); + } + }); } } catch (error) { entity.data = {}; diff --git a/packages/core/src/activity-log/dto/get-activity-logs.dto.ts b/packages/core/src/activity-log/dto/get-activity-logs.dto.ts index b0b0d40d614..ef3dc1b2785 100644 --- a/packages/core/src/activity-log/dto/get-activity-logs.dto.ts +++ b/packages/core/src/activity-log/dto/get-activity-logs.dto.ts @@ -1,6 +1,6 @@ import { ApiPropertyOptional, IntersectionType, PickType } from '@nestjs/swagger'; import { IsEnum, IsIn, IsOptional, IsString, IsUUID } from 'class-validator'; -import { ActionTypeEnum, EntityEnum, ActorTypeEnum, ID } from '@gauzy/contracts'; +import { ActionTypeEnum, BaseEntityEnum, ActorTypeEnum, ID } from '@gauzy/contracts'; import { PaginationParams } from '../../core/crud'; import { TenantOrganizationBaseDTO } from '../../core/dto'; import { ActivityLog } from '../activity-log.entity'; @@ -18,10 +18,10 @@ export class GetActivityLogsDTO extends IntersectionType( PickType(ActivityLog, ['isActive', 'isArchived']) ) { // Filter by entity (example: Organization, Task, OrganizationContact) - @ApiPropertyOptional({ enum: EntityEnum }) + @ApiPropertyOptional({ enum: BaseEntityEnum }) @IsOptional() - @IsEnum(EntityEnum) - entity: EntityEnum; + @IsEnum(BaseEntityEnum) + entity: BaseEntityEnum; // Filter by entityId (example: projectId, taskId, organizationContactId) @ApiPropertyOptional({ type: () => String }) diff --git a/packages/core/src/comment/comment.entity.ts b/packages/core/src/comment/comment.entity.ts index 1b99250913b..2d26069e81b 100644 --- a/packages/core/src/comment/comment.entity.ts +++ b/packages/core/src/comment/comment.entity.ts @@ -3,7 +3,7 @@ import { EntityRepositoryType } from '@mikro-orm/core'; import { JoinColumn, JoinTable, RelationId } from 'typeorm'; import { IsArray, IsBoolean, IsDate, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator'; import { Type } from 'class-transformer'; -import { ActorTypeEnum, CommentEntityEnum, IComment, ID, IEmployee, IOrganizationTeam, IUser } from '@gauzy/contracts'; +import { ActorTypeEnum, BaseEntityEnum, IComment, ID, IEmployee, IOrganizationTeam, IUser } from '@gauzy/contracts'; import { Employee, OrganizationTeam, TenantOrganizationBaseEntity, User } from '../core/entities/internal'; import { ColumnIndex, @@ -19,12 +19,12 @@ import { MikroOrmCommentRepository } from './repository/mikro-orm-comment.reposi export class Comment extends TenantOrganizationBaseEntity implements IComment { [EntityRepositoryType]?: MikroOrmCommentRepository; - @ApiProperty({ type: () => String, enum: CommentEntityEnum }) + @ApiProperty({ type: () => String, enum: BaseEntityEnum }) @IsNotEmpty() - @IsEnum(CommentEntityEnum) + @IsEnum(BaseEntityEnum) @ColumnIndex() @MultiORMColumn() - entity: CommentEntityEnum; + entity: BaseEntityEnum; // Indicate the ID of entity record commented @ApiProperty({ type: () => String }) diff --git a/packages/core/src/core/decorators/is-favoritable.ts b/packages/core/src/core/decorators/is-favoritable.ts index d29a116bb08..acc4924a84d 100644 --- a/packages/core/src/core/decorators/is-favoritable.ts +++ b/packages/core/src/core/decorators/is-favoritable.ts @@ -1,7 +1,7 @@ -import { EntityEnum } from '@gauzy/contracts'; +import { BaseEntityEnum } from '@gauzy/contracts'; import { SetMetadata } from '@nestjs/common'; export const FAVORITE_SERVICE = 'FAVORITE_SERVICE'; export const FAVORITABLE_TYPE = 'favoriteEntity'; -export const FavoriteService = (type: EntityEnum) => SetMetadata(FAVORITABLE_TYPE, type); +export const FavoriteService = (type: BaseEntityEnum) => SetMetadata(FAVORITABLE_TYPE, type); diff --git a/packages/core/src/favorite/favorite.entity.ts b/packages/core/src/favorite/favorite.entity.ts index 8ba49dc0984..161086165c3 100644 --- a/packages/core/src/favorite/favorite.entity.ts +++ b/packages/core/src/favorite/favorite.entity.ts @@ -2,7 +2,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { EntityRepositoryType } from '@mikro-orm/core'; import { JoinColumn, RelationId } from 'typeorm'; import { IsEnum, IsNotEmpty, IsOptional, IsUUID } from 'class-validator'; -import { EntityEnum, ID, IEmployee, IFavorite } from '@gauzy/contracts'; +import { BaseEntityEnum, ID, IEmployee, IFavorite } from '@gauzy/contracts'; import { Employee, TenantOrganizationBaseEntity } from '../core/entities/internal'; import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToOne } from '../core/decorators/entity'; import { MikroOrmFavoriteRepository } from './repository/mikro-orm-favorite.repository'; @@ -12,12 +12,12 @@ export class Favorite extends TenantOrganizationBaseEntity implements IFavorite [EntityRepositoryType]?: MikroOrmFavoriteRepository; // Indicate the entity type - @ApiProperty({ type: () => String, enum: EntityEnum }) + @ApiProperty({ type: () => String, enum: BaseEntityEnum }) @IsNotEmpty() - @IsEnum(EntityEnum) + @IsEnum(BaseEntityEnum) @ColumnIndex() @MultiORMColumn() - entity: EntityEnum; + entity: BaseEntityEnum; // Indicate the ID of entity record marked as favorite @ApiProperty({ type: () => String }) diff --git a/packages/core/src/favorite/favorite.service.ts b/packages/core/src/favorite/favorite.service.ts index 59e63bf1d85..f66d1eeb26c 100644 --- a/packages/core/src/favorite/favorite.service.ts +++ b/packages/core/src/favorite/favorite.service.ts @@ -1,6 +1,6 @@ import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { DeleteResult, FindOptionsWhere, In } from 'typeorm'; -import { EntityEnum, ID, IFavorite, IFavoriteCreateInput, IPagination } from '@gauzy/contracts'; +import { BaseEntityEnum, ID, IFavorite, IFavoriteCreateInput, IPagination } from '@gauzy/contracts'; import { PaginationParams, TenantAwareCrudService } from './../core/crud'; import { RequestContext } from '../core/context'; import { Favorite } from './favorite.entity'; @@ -109,7 +109,7 @@ export class FavoriteService extends TenantAwareCrudService { try { const { where } = options; const { entity } = where; - const favoriteType: EntityEnum = entity as EntityEnum; + const favoriteType: BaseEntityEnum = entity as BaseEntityEnum; // Find favorite elements with filtered params const favorites = await super.findAll(options); diff --git a/packages/core/src/favorite/global-favorite-service.service.ts b/packages/core/src/favorite/global-favorite-service.service.ts index ff59407e300..3fbe49d1f95 100644 --- a/packages/core/src/favorite/global-favorite-service.service.ts +++ b/packages/core/src/favorite/global-favorite-service.service.ts @@ -1,12 +1,12 @@ import { BadRequestException, Injectable, InternalServerErrorException, OnModuleInit } from '@nestjs/common'; import { DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core'; import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; -import { EntityEnum } from '@gauzy/contracts'; +import { BaseEntityEnum } from '@gauzy/contracts'; import { FAVORITABLE_TYPE } from '../core/decorators/is-favoritable'; @Injectable() export class GlobalFavoriteDiscoveryService implements OnModuleInit { - private readonly serviceMap = new Map(); + private readonly serviceMap = new Map(); constructor( private readonly discoveryService: DiscoveryService, @@ -39,16 +39,16 @@ export class GlobalFavoriteDiscoveryService implements OnModuleInit { } // Extract service favorite type - private extractTypeFromProvider(metatype: any): EntityEnum | null { + private extractTypeFromProvider(metatype: any): BaseEntityEnum | null { return Reflect.getMetadata(FAVORITABLE_TYPE, metatype); } // Get "Favoritable" service - getService(type: EntityEnum) { + getService(type: BaseEntityEnum) { return this.serviceMap.get(type); } - callMethod(type: EntityEnum, methodName: string, ...args: any[]): any { + callMethod(type: BaseEntityEnum, methodName: string, ...args: any[]): any { const serviceWithMethods = this.serviceMap.get(type); if (!serviceWithMethods) { throw new BadRequestException(`Service for type ${type} not found`); diff --git a/packages/core/src/organization-project-module/organization-project-module.service.ts b/packages/core/src/organization-project-module/organization-project-module.service.ts index f0205c7b318..c6cf041e4ce 100644 --- a/packages/core/src/organization-project-module/organization-project-module.service.ts +++ b/packages/core/src/organization-project-module/organization-project-module.service.ts @@ -3,7 +3,7 @@ import { EventBus } from '@nestjs/cqrs'; import { Brackets, FindManyOptions, SelectQueryBuilder, UpdateResult, WhereExpressionBuilder } from 'typeorm'; import { ActionTypeEnum, - EntityEnum, + BaseEntityEnum, ActorTypeEnum, ID, IOrganizationProjectModule, @@ -54,14 +54,14 @@ export class OrganizationProjectModuleService extends TenantAwareCrudService { constructor( @@ -127,14 +127,14 @@ export class OrganizationProjectService extends TenantAwareCrudService { constructor( @@ -118,14 +118,14 @@ export class OrganizationSprintService extends TenantAwareCrudService { constructor( diff --git a/packages/core/src/resource-link/resource-link.entity.ts b/packages/core/src/resource-link/resource-link.entity.ts index bb84ae49767..0a4e79f5928 100644 --- a/packages/core/src/resource-link/resource-link.entity.ts +++ b/packages/core/src/resource-link/resource-link.entity.ts @@ -2,7 +2,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { EntityRepositoryType } from '@mikro-orm/core'; import { JoinColumn, RelationId } from 'typeorm'; import { IsEnum, IsNotEmpty, IsOptional, IsString, IsUrl, IsUUID } from 'class-validator'; -import { EntityEnum, ID, IResourceLink, IURLMetaData, IUser } from '@gauzy/contracts'; +import { BaseEntityEnum, ID, IResourceLink, IURLMetaData, IUser } from '@gauzy/contracts'; import { isBetterSqlite3, isSqlite } from '@gauzy/config'; import { TenantOrganizationBaseEntity, User } from '../core/entities/internal'; import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToOne } from '../core/decorators/entity'; @@ -12,12 +12,12 @@ import { MikroOrmResourceLinkRepository } from './repository/mikro-orm-resource- export class ResourceLink extends TenantOrganizationBaseEntity implements IResourceLink { [EntityRepositoryType]?: MikroOrmResourceLinkRepository; - @ApiProperty({ type: () => String, enum: EntityEnum }) + @ApiProperty({ type: () => String, enum: BaseEntityEnum }) @IsNotEmpty() - @IsEnum(EntityEnum) + @IsEnum(BaseEntityEnum) @ColumnIndex() @MultiORMColumn() - entity: EntityEnum; + entity: BaseEntityEnum; @ApiProperty({ type: () => String }) @IsNotEmpty() diff --git a/packages/core/src/resource-link/resource-link.service.ts b/packages/core/src/resource-link/resource-link.service.ts index a9d65a0231c..ba8b13a492b 100644 --- a/packages/core/src/resource-link/resource-link.service.ts +++ b/packages/core/src/resource-link/resource-link.service.ts @@ -7,7 +7,7 @@ import { IResourceLinkUpdateInput, ID, ActionTypeEnum, - EntityEnum, + BaseEntityEnum, ActorTypeEnum } from '@gauzy/contracts'; import { TenantAwareCrudService } from './../core/crud'; @@ -58,14 +58,14 @@ export class ResourceLinkService extends TenantAwareCrudService { // Generate the activity log description. const description = generateActivityLogDescription( ActionTypeEnum.Created, - EntityEnum.ResourceLink, + BaseEntityEnum.ResourceLink, `${resourceLink.title} for ${resourceLink.entity}` ); // Emit an event to log the activity this._eventBus.publish( new ActivityLogEvent({ - entity: EntityEnum.ResourceLink, + entity: BaseEntityEnum.ResourceLink, entityId: resourceLink.id, action: ActionTypeEnum.Created, actorType: ActorTypeEnum.User, @@ -105,7 +105,7 @@ export class ResourceLinkService extends TenantAwareCrudService { // Generate the activity log description. const description = generateActivityLogDescription( ActionTypeEnum.Updated, - EntityEnum.ResourceLink, + BaseEntityEnum.ResourceLink, `${resourceLink.title} for ${resourceLink.entity}` ); @@ -118,7 +118,7 @@ export class ResourceLinkService extends TenantAwareCrudService { // Emit event to log activity this._eventBus.publish( new ActivityLogEvent({ - entity: EntityEnum.ResourceLink, + entity: BaseEntityEnum.ResourceLink, entityId: updatedResourceLink.id, action: ActionTypeEnum.Updated, actorType: ActorTypeEnum.User, diff --git a/packages/core/src/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/tasks/commands/handlers/task-create.handler.ts index 6d66f508730..2595294cad9 100644 --- a/packages/core/src/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/tasks/commands/handlers/task-create.handler.ts @@ -1,6 +1,6 @@ import { CommandHandler, ICommandHandler, EventBus as CqrsEventBus } from '@nestjs/cqrs'; import { HttpException, HttpStatus, Logger } from '@nestjs/common'; -import { ActionTypeEnum, EntityEnum, ActorTypeEnum, ITask } from '@gauzy/contracts'; +import { ActionTypeEnum, BaseEntityEnum, ActorTypeEnum, ITask } from '@gauzy/contracts'; import { ActivityLogEvent } from '../../../activity-log/events'; import { generateActivityLogDescription } from '../../../activity-log/activity-log.helper'; import { EventBus } from '../../../event-bus'; @@ -75,14 +75,14 @@ export class TaskCreateHandler implements ICommandHandler { } // Generate the activity log description - const description = generateActivityLogDescription(ActionTypeEnum.Created, EntityEnum.Task, task.title); + const description = generateActivityLogDescription(ActionTypeEnum.Created, BaseEntityEnum.Task, task.title); console.log(`Generating activity log description: ${description}`); // Emit an event to log the activity this._cqrsEventBus.publish( new ActivityLogEvent({ - entity: EntityEnum.Task, + entity: BaseEntityEnum.Task, entityId: task.id, action: ActionTypeEnum.Created, actorType: ActorTypeEnum.User, // TODO : Since we have Github Integration, make sure we can also store "System" for actor @@ -95,7 +95,7 @@ export class TaskCreateHandler implements ICommandHandler { console.log( `Task created with ID: ${task.id} with activity log: ${JSON.stringify({ - entity: EntityEnum.Task, + entity: BaseEntityEnum.Task, entityId: task.id, action: ActionTypeEnum.Created, actorType: ActorTypeEnum.User, // TODO : Since we have Github Integration, make sure we can also store "System" for actor diff --git a/packages/core/src/tasks/task.service.ts b/packages/core/src/tasks/task.service.ts index c1e27c5cd54..e478ab1d6ac 100644 --- a/packages/core/src/tasks/task.service.ts +++ b/packages/core/src/tasks/task.service.ts @@ -14,7 +14,7 @@ import { import { isBoolean, isUUID } from 'class-validator'; import { ActionTypeEnum, - EntityEnum, + BaseEntityEnum, ActorTypeEnum, ID, IEmployee, @@ -104,7 +104,7 @@ export class TaskService extends TenantAwareCrudService { // Generate the activity log description const description = generateActivityLogDescription( ActionTypeEnum.Updated, - EntityEnum.Task, + BaseEntityEnum.Task, updatedTask.title ); @@ -116,7 +116,7 @@ export class TaskService extends TenantAwareCrudService { // Emit an event to log the activity this._eventBus.publish( new ActivityLogEvent({ - entity: EntityEnum.Task, + entity: BaseEntityEnum.Task, entityId: updatedTask.id, action: ActionTypeEnum.Updated, actorType: ActorTypeEnum.User, // TODO : Since we have Github Integration, make sure we can also store "System" for actor diff --git a/packages/core/src/tasks/views/view.service.ts b/packages/core/src/tasks/views/view.service.ts index 3034f985a0d..749c3bd5906 100644 --- a/packages/core/src/tasks/views/view.service.ts +++ b/packages/core/src/tasks/views/view.service.ts @@ -4,7 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { ActionTypeEnum, ActorTypeEnum, - EntityEnum, + BaseEntityEnum, ID, ITaskView, ITaskViewCreateInput, @@ -22,7 +22,7 @@ import { TaskView } from './view.entity'; import { TypeOrmTaskViewRepository } from './repository/type-orm-task-view.repository'; import { MikroOrmTaskViewRepository } from './repository/mikro-orm-task-view.repository'; -@FavoriteService(EntityEnum.TaskView) +@FavoriteService(BaseEntityEnum.TaskView) @Injectable() export class TaskViewService extends TenantAwareCrudService { constructor( @@ -50,12 +50,16 @@ export class TaskViewService extends TenantAwareCrudService { const view = await super.create({ ...entity, tenantId }); // Generate the activity log description. - const description = generateActivityLogDescription(ActionTypeEnum.Created, EntityEnum.TaskView, view.name); + const description = generateActivityLogDescription( + ActionTypeEnum.Created, + BaseEntityEnum.TaskView, + view.name + ); // Emit an event to log the activity this._eventBus.publish( new ActivityLogEvent({ - entity: EntityEnum.TaskView, + entity: BaseEntityEnum.TaskView, entityId: view.id, action: ActionTypeEnum.Created, actorType: ActorTypeEnum.User, @@ -94,7 +98,7 @@ export class TaskViewService extends TenantAwareCrudService { // Generate the activity log description. const description = generateActivityLogDescription( ActionTypeEnum.Updated, - EntityEnum.TaskView, + BaseEntityEnum.TaskView, updatedTaskView.name ); @@ -107,7 +111,7 @@ export class TaskViewService extends TenantAwareCrudService { // Emit event to log activity this._eventBus.publish( new ActivityLogEvent({ - entity: EntityEnum.TaskView, + entity: BaseEntityEnum.TaskView, entityId: updatedTaskView.id, action: ActionTypeEnum.Updated, actorType: ActorTypeEnum.User, From 14ab545dfde33dc0a03ada8ef697dd77a5c63ac0 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 14 Oct 2024 10:36:10 +0200 Subject: [PATCH 6/7] fix: CodeRabbit suggestions --- .../resource-link/resource-link.service.ts | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/packages/core/src/resource-link/resource-link.service.ts b/packages/core/src/resource-link/resource-link.service.ts index ba8b13a492b..f4f1a928bac 100644 --- a/packages/core/src/resource-link/resource-link.service.ts +++ b/packages/core/src/resource-link/resource-link.service.ts @@ -61,20 +61,7 @@ export class ResourceLinkService extends TenantAwareCrudService { BaseEntityEnum.ResourceLink, `${resourceLink.title} for ${resourceLink.entity}` ); - - // Emit an event to log the activity - this._eventBus.publish( - new ActivityLogEvent({ - entity: BaseEntityEnum.ResourceLink, - entityId: resourceLink.id, - action: ActionTypeEnum.Created, - actorType: ActorTypeEnum.User, - description, - data: resourceLink, - organizationId: resourceLink.organizationId, - tenantId - }) - ); + this.logActivity(ActionTypeEnum.Created, resourceLink, description); return resourceLink; } catch (error) { @@ -102,6 +89,12 @@ export class ResourceLinkService extends TenantAwareCrudService { id }); + // Compare values before and after update then add updates to fields + const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues( + updatedResourceLink, + input + ); + // Generate the activity log description. const description = generateActivityLogDescription( ActionTypeEnum.Updated, @@ -109,27 +102,14 @@ export class ResourceLinkService extends TenantAwareCrudService { `${resourceLink.title} for ${resourceLink.entity}` ); - // Compare values before and after update then add updates to fields - const { updatedFields, previousValues, updatedValues } = activityLogUpdatedFieldsAndValues( + //Log activity + this.logActivity( + ActionTypeEnum.Updated, updatedResourceLink, - input - ); - - // Emit event to log activity - this._eventBus.publish( - new ActivityLogEvent({ - entity: BaseEntityEnum.ResourceLink, - entityId: updatedResourceLink.id, - action: ActionTypeEnum.Updated, - actorType: ActorTypeEnum.User, - description, - updatedFields, - updatedValues, - previousValues, - data: updatedResourceLink, - organizationId: updatedResourceLink.organizationId, - tenantId: updatedResourceLink.tenantId - }) + description, + updatedFields, + previousValues, + updatedValues ); // return updated Resource Link @@ -139,4 +119,29 @@ export class ResourceLinkService extends TenantAwareCrudService { throw new BadRequestException('Resource Link update failed', error); } } + + private logActivity( + action: ActionTypeEnum, + resourceLink: ResourceLink, + description: string, + updatedFields?: string[], + previousValues?: any, + updatedValues?: any + ) { + this._eventBus.publish( + new ActivityLogEvent({ + entity: BaseEntityEnum.ResourceLink, + entityId: resourceLink.id, + action, + actorType: ActorTypeEnum.User, + description, + updatedFields, + updatedValues, + previousValues, + data: resourceLink, + organizationId: resourceLink.organizationId, + tenantId: resourceLink.tenantId + }) + ); + } } From b6b097a91cd0ead92dd0bcd610f69fe0baa8e8da Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Mon, 14 Oct 2024 11:19:39 +0200 Subject: [PATCH 7/7] fix: CodeRabbit suggestions --- .../activity-log/activity-log.subscriber.ts | 57 +++++++++++++++---- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/packages/core/src/activity-log/activity-log.subscriber.ts b/packages/core/src/activity-log/activity-log.subscriber.ts index aa658ac073f..77313c327b0 100644 --- a/packages/core/src/activity-log/activity-log.subscriber.ts +++ b/packages/core/src/activity-log/activity-log.subscriber.ts @@ -13,6 +13,32 @@ export class ActivityLogSubscriber extends BaseEntityEventSubscriber { + if (Array.isArray(entity[field]) || typeof entity[field] === 'object') { + entity[field] = JSON.stringify(entity[field]); + } + }); + } + + /** + * @description de-serialize Activity Log fields to support SQLite DB after load data + * @param {ActivityLog} entity - The ActivityLog entity that is about to be loaded or updated. + * @param {string[]} fields - Array fields to be de-serialized + */ + private deserializeFields(entity: ActivityLog, fields: string[]): void { + fields.forEach((field) => { + if (entity[field] && typeof entity[field] === 'string') { + entity[field] = JSON.parse(entity[field]); + } + }); + } + /** * Called before an ActivityLog entity is inserted or updated in the database. * This method prepares the entity for insertion or update by serializing the data property to a JSON string @@ -31,15 +57,20 @@ export class ActivityLogSubscriber extends BaseEntityEventSubscriber { - if (Array.isArray(entity[field]) || typeof entity[field] === 'object') { - entity[field] = JSON.stringify(entity[field]); - } - }); + this.serializeFields(entity, [ + 'updatedValues', + 'previousValues', + 'updatedEntities', + 'previousEntities' + ]); } } catch (error) { // Log the error and reset the data to an empty object if JSON parsing fails console.error('Error stringify data in serializeDataForSQLite:', error); + entity.data = '{}'; + ['updatedValues', 'previousValues', 'updatedEntities', 'previousEntities'].forEach((field) => { + entity[field] = '{}'; + }); } } @@ -84,16 +115,20 @@ export class ActivityLogSubscriber extends BaseEntityEventSubscriber { - if (entity[field] && typeof entity[field] === 'string') { - entity[field] = JSON.parse(entity[field]); - } - }); + this.deserializeFields(entity, [ + 'updatedValues', + 'previousValues', + 'updatedEntities', + 'previousEntities' + ]); } } catch (error) { - entity.data = {}; // Log the error and reset the data to an empty object if JSON parsing fails console.error('Error parsing JSON data in afterEntityLoad:', error); + entity.data = {}; + ['updatedValues', 'previousValues', 'updatedEntities', 'previousEntities'].forEach((field) => { + entity[field] = {}; + }); } } }