Skip to content

Commit

Permalink
Merge pull request #8419 from ever-co/fix/actor-type-transformer-integer
Browse files Browse the repository at this point in the history
[Fix] Integer Storage for ActorTypeEnum in Database
  • Loading branch information
rahul-rocket authored Oct 14, 2024
2 parents ad84931 + 8060c55 commit 9b5eaea
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 53 deletions.
4 changes: 2 additions & 2 deletions packages/contracts/src/base-entity.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ export interface IBasePerTenantAndOrganizationEntityMutationInput extends Partia

// Actor type defines if it's User or system performed some action
export enum ActorTypeEnum {
System = 0, // System performed the action
User = 1 // User performed the action
System = 'System', // System performed the action
User = 'User' // User performed the action
}

export enum EntityEnum {
Expand Down
8 changes: 3 additions & 5 deletions packages/core/src/activity-log/activity-log.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ActivityLogService } from './activity-log.service';
@Permissions()
@Controller('/activity-log')
export class ActivityLogController {
constructor(readonly _activityLogService: ActivityLogService) {}
constructor(readonly _activityLogService: ActivityLogService) {}

/**
* Retrieves activity logs based on query parameters.
Expand All @@ -20,10 +20,8 @@ export class ActivityLogController {
* @returns A list of activity logs.
*/
@Get('/')
@UseValidationPipe()
async getActivityLogs(
@Query() query: GetActivityLogsDTO
): Promise<IPagination<IActivityLog>> {
@UseValidationPipe({ transform: true })
async getActivityLogs(@Query() query: GetActivityLogsDTO): Promise<IPagination<IActivityLog>> {
return await this._activityLogService.findActivityLogs(query);
}
}
9 changes: 5 additions & 4 deletions packages/core/src/activity-log/activity-log.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { isMySQL, isPostgres } from '@gauzy/config';
import { EntityEnum, 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 { ActorTypeTransformerPipe } from '../shared/pipes';
import { MikroOrmActivityLogRepository } from './repository/mikro-orm-activity-log.repository';

@MultiORMEntity('activity_log', { mikroOrmRepository: () => MikroOrmActivityLogRepository })
Expand All @@ -26,19 +27,19 @@ export class ActivityLog extends TenantOrganizationBaseEntity implements IActivi
@MultiORMColumn()
entityId: ID;

@ApiProperty({ type: () => String, enum: ActionTypeEnum })
@ApiProperty({ enum: ActionTypeEnum })
@IsNotEmpty()
@IsEnum(ActionTypeEnum)
@ColumnIndex()
@MultiORMColumn()
action: ActionTypeEnum;

@ApiPropertyOptional({ type: () => String, enum: ActorTypeEnum })
@ApiPropertyOptional({ enum: ActorTypeEnum })
@IsOptional()
@IsEnum(ActorTypeEnum)
@ColumnIndex()
@MultiORMColumn({ nullable: true })
actorType?: ActorTypeEnum;
@MultiORMColumn({ type: 'int', nullable: true, transformer: new ActorTypeTransformerPipe() })
actorType?: ActorTypeEnum; // Will be stored as 0 or 1 in DB

@ApiPropertyOptional({ type: () => String })
@IsOptional()
Expand Down
49 changes: 28 additions & 21 deletions packages/core/src/activity-log/activity-log.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { FindManyOptions, FindOptionsOrder, FindOptionsWhere } from 'typeorm';
import { IActivityLog, IActivityLogInput, IPagination } from '@gauzy/contracts';
import { isNotNullOrUndefined } from '@gauzy/common';
import { TenantAwareCrudService } from './../core/crud';
import { RequestContext } from '../core/context';
import { GetActivityLogsDTO, allowedOrderDirections, allowedOrderFields } from './dto/get-activity-logs.dto';
Expand All @@ -17,9 +18,9 @@ export class ActivityLogService extends TenantAwareCrudService<ActivityLog> {
}

/**
* Finds and retrieves activity logs based on the given filter criteria.
* Finds and retrieves activity logs based on the given filters criteria.
*
* @param {GetActivityLogsDTO} filter - Filter criteria to find activity logs, including entity, entityId, action, actorType, isActive, isArchived, orderBy, and order.
* @param {GetActivityLogsDTO} filters - Filter criteria to find activity logs, including entity, entityId, action, actorType, isActive, isArchived, orderBy, and order.
* @returns {Promise<IPagination<IActivityLog>>} - A promise that resolves to a paginated list of activity logs.
*
* Example usage:
Expand All @@ -32,8 +33,9 @@ export class ActivityLogService extends TenantAwareCrudService<ActivityLog> {
* });
* ```
*/
public async findActivityLogs(filter: GetActivityLogsDTO): Promise<IPagination<IActivityLog>> {
public async findActivityLogs(filters: GetActivityLogsDTO): Promise<IPagination<IActivityLog>> {
const {
organizationId,
entity,
entityId,
action,
Expand All @@ -42,39 +44,44 @@ export class ActivityLogService extends TenantAwareCrudService<ActivityLog> {
isArchived = false,
orderBy = 'createdAt',
order = 'DESC',
relations = [],
skip,
take
} = filter;
relations = []
} = filters;

// Fallback to default if invalid orderBy/order values are provided
const orderField = allowedOrderFields.includes(orderBy) ? orderBy : 'createdAt';
const orderDirection = allowedOrderDirections.includes(order.toUpperCase()) ? order.toUpperCase() : 'DESC';

// Define order option
const orderOption: FindOptionsOrder<ActivityLog> = { [orderField]: orderDirection };

// Build the 'where' condition using concise syntax
const where: FindOptionsWhere<ActivityLog> = {
...(organizationId && { organizationId }),
...(entity && { entity }),
...(entityId && { entityId }),
...(action && { action }),
...(actorType && { actorType }),
...(isNotNullOrUndefined(actorType) && { actorType }),
isActive,
isArchived
};

// Fallback to default if invalid orderBy/order values are provided
const orderField = allowedOrderFields.includes(orderBy) ? orderBy : 'createdAt';
const orderDirection = allowedOrderDirections.includes(order.toUpperCase()) ? order.toUpperCase() : 'DESC';
const take = filters.take ? filters.take : 100; // Default take value if not provided
// Pagination: ensure `filters.skip` is a positive integer starting from 1
const skip = (filters.skip && Number.isInteger(filters.skip) && filters.skip > 0) ? filters.skip : 1;

// Define order option
const orderOption: FindOptionsOrder<ActivityLog> = { [orderField]: orderDirection };

// Define find options
const findOptions: FindManyOptions<ActivityLog> = {
// Ensure that filters are properly defined
const queryOptions: FindManyOptions<ActivityLog> = {
where,
order: orderOption,
...(skip && { skip }),
...(take && { take }),
...(relations && { relations })
...(relations && { relations }),
take: take,
skip: take * (skip - 1) // Calculate offset (skip) based on validated skip value
};

// Apply sorting options (if provided)
queryOptions.order = orderOption;

// Retrieve activity logs using the base class method
return await super.findAll(findOptions);
return await super.findAll(queryOptions);
}

/**
Expand Down
10 changes: 2 additions & 8 deletions packages/core/src/activity-log/dto/get-activity-logs.dto.ts
Original file line number Diff line number Diff line change
@@ -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, EntityEnum, ID } from '@gauzy/contracts';
import { PaginationParams } from '../../core/crud';
import { TenantOrganizationBaseDTO } from '../../core/dto';
import { ActivityLog } from '../activity-log.entity';
Expand All @@ -15,7 +15,7 @@ export const allowedOrderDirections = ['ASC', 'DESC', 'asc', 'desc'];
export class GetActivityLogsDTO extends IntersectionType(
TenantOrganizationBaseDTO,
PickType(PaginationParams<ActivityLog>, ['skip', 'take', 'relations']),
PickType(ActivityLog, ['isActive', 'isArchived'])
PickType(ActivityLog, ['isActive', 'isArchived', 'actorType'])
) {
// Filter by entity (example: Organization, Task, OrganizationContact)
@ApiPropertyOptional({ enum: EntityEnum })
Expand All @@ -35,12 +35,6 @@ export class GetActivityLogsDTO extends IntersectionType(
@IsEnum(ActionTypeEnum)
action: ActionTypeEnum;

// Filter by actorType (example: SYSTEM, USER)
@ApiPropertyOptional({ type: () => String, enum: ActorTypeEnum })
@IsOptional()
@IsEnum(ActorTypeEnum)
actorType?: ActorTypeEnum;

// Filter by orderBy (example: createdAt, updatedAt, entity, action)
@ApiPropertyOptional({ type: () => String, enum: allowedOrderFields })
@IsOptional()
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/comment/comment.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
MultiORMManyToOne,
MultiORMOneToMany
} from '../core/decorators/entity';
import { ActorTypeTransformerPipe } from '../shared/pipes';
import { MikroOrmCommentRepository } from './repository/mikro-orm-comment.repository';

@MultiORMEntity('comment', { mikroOrmRepository: () => MikroOrmCommentRepository })
Expand Down Expand Up @@ -40,12 +41,12 @@ export class Comment extends TenantOrganizationBaseEntity implements IComment {
@MultiORMColumn({ type: 'text' })
comment: string;

@ApiPropertyOptional({ type: () => String, enum: ActorTypeEnum })
@IsNotEmpty()
@ApiPropertyOptional({ enum: ActorTypeEnum })
@IsOptional()
@IsEnum(ActorTypeEnum)
@ColumnIndex()
@MultiORMColumn({ nullable: true })
actorType?: ActorTypeEnum;
@MultiORMColumn({ type: 'int', nullable: true, transformer: new ActorTypeTransformerPipe() })
actorType?: ActorTypeEnum; // Will be stored as 0 or 1 in DB

@ApiPropertyOptional({ type: Boolean })
@IsOptional()
Expand Down
28 changes: 28 additions & 0 deletions packages/core/src/shared/pipes/actor-type-transform.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ValueTransformer } from 'typeorm';
import { ActorTypeEnum } from '@gauzy/contracts';

/**
* ActorTypeTransformerPipe handles the conversion between the enum string values
* (used in the application) and the integer values (stored in the database).
*/
export class ActorTypeTransformerPipe implements ValueTransformer {
/**
* Converts the enum string value to its integer representation when writing to the database.
*
* @param value - The `ActorTypeEnum` value ('System' or 'User').
* @returns The corresponding integer value to be stored in the database (0 for System, 1 for User).
*/
to(value: ActorTypeEnum): number {
return value === ActorTypeEnum.User ? 1 : 0; // 1 for 'User', 0 for 'System' (default)
}

/**
* Converts the integer value to its corresponding `ActorTypeEnum` string when reading from the database.
*
* @param value - The integer value (0 or 1) from the database.
* @returns The corresponding `ActorTypeEnum` ('System' for 0, 'User' for 1).
*/
from(value: number): ActorTypeEnum {
return value === 1 ? ActorTypeEnum.User : ActorTypeEnum.System;
}
}
9 changes: 5 additions & 4 deletions packages/core/src/shared/pipes/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export * from './uuid-validation.pipe';
export * from './parse-json.pipe';
export * from './abstract-validation.pipe';
export * from './actor-type-transform.pipe';
export * from './bulk-body-load-transform.pipe';
export * from './column-numeric-transformer.pipe';
export * from './abstract-validation.pipe';
export * from './use-validation-pipe.pipe';
export * from './http-method-transformer.pipe';
export * from './parse-json.pipe';
export * from './use-validation.pipe';
export * from './uuid-validation.pipe';
5 changes: 0 additions & 5 deletions packages/core/src/shared/pipes/use-validation-pipe.pipe.ts

This file was deleted.

15 changes: 15 additions & 0 deletions packages/core/src/shared/pipes/use-validation.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UsePipes, ValidationPipe, ValidationPipeOptions } from '@nestjs/common';

/**
* Creates and applies a custom validation pipe with optional configuration.
*
* This function is a helper for applying NestJS's `ValidationPipe` with custom options
* to a route or controller. It wraps the `UsePipes` decorator and makes it easier to
* customize validation behavior.
*
* @param options - Optional `ValidationPipeOptions` to customize the validation behavior.
* @returns A decorator that applies the `ValidationPipe` with the given options.
*/
export function UseValidationPipe(options?: Partial<ValidationPipeOptions>) {
return UsePipes(new ValidationPipe(options ?? {}));
}

0 comments on commit 9b5eaea

Please sign in to comment.