Skip to content

Commit

Permalink
fix: improve ActorTypeTransformerPipe for integer storage
Browse files Browse the repository at this point in the history
  • Loading branch information
rahul-rocket committed Oct 14, 2024
1 parent ad84931 commit c11fd85
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 45 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
31 changes: 18 additions & 13 deletions packages/core/src/activity-log/activity-log.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,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,17 +43,16 @@ export class ActivityLogService extends TenantAwareCrudService<ActivityLog> {
isArchived = false,
orderBy = 'createdAt',
order = 'DESC',
relations = [],
skip,
take
} = filter;
relations = []
} = filters;

// Build the 'where' condition using concise syntax
const where: FindOptionsWhere<ActivityLog> = {
...(entity && { entity }),
...(entityId && { entityId }),
...(action && { action }),
...(actorType && { actorType }),
...(actorType !== undefined && actorType !== null && { actorType }), // Ensure 0 is not ignored
organizationId,
isActive,
isArchived
};
Expand All @@ -64,17 +64,22 @@ export class ActivityLogService extends TenantAwareCrudService<ActivityLog> {
// 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: filters.take ?? 100, // Default to 100 if not provided
skip: filters.skip ? filters.take * (filters.skip - 1) : 0 // Calculate offset
};

// Apply sorting options (if provided)
if (filters.order) {
queryOptions.order = orderOption; // Order, in which entities should be ordered. Default to ASC if no order is provided.
}

console.log('queryOptions', queryOptions);
// 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
33 changes: 33 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,33 @@
import { Logger } from '@nestjs/common';
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 {
private readonly logger = new Logger(ActorTypeTransformerPipe.name);

/**
* 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 {
this.logger.debug(`ActorTypeTransformerPipe: converting ${value} to integer`);
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 {
this.logger.debug(`ActorTypeTransformerPipe: converting ${value} to enum`);
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 c11fd85

Please sign in to comment.