diff --git a/packages/contracts/src/lib/base-entity.model.ts b/packages/contracts/src/lib/base-entity.model.ts index cf415ddbd75..1462d0fddff 100644 --- a/packages/contracts/src/lib/base-entity.model.ts +++ b/packages/contracts/src/lib/base-entity.model.ts @@ -80,6 +80,7 @@ export enum ActorTypeEnum { User = 'User' // User performed the action } +// BaseEntityEnum defines the type of the entity, used in BaseEntity model export enum BaseEntityEnum { Candidate = 'Candidate', Comment = 'Comment', diff --git a/packages/contracts/src/lib/comment.model.ts b/packages/contracts/src/lib/comment.model.ts index c35f909a933..1c9a42867f7 100644 --- a/packages/contracts/src/lib/comment.model.ts +++ b/packages/contracts/src/lib/comment.model.ts @@ -1,36 +1,34 @@ import { ActorTypeEnum, IBasePerEntityType, ID } from './base-entity.model'; -import { IUser } from './user.model'; -import { IEmployee } from './employee.model'; +import { IEmployee, IEmployeeEntityInput as ICommentAuthor } from './employee.model'; import { IOrganizationTeam } from './organization-team.model'; -import { IMentionUserIds } from './mention.model'; +import { IMentionEmployeeIds } from './mention.model'; -export interface IComment extends IBasePerEntityType { +export interface IComment extends IBasePerEntityType, ICommentAuthor { comment: string; - creator?: IUser; - creatorId?: ID; // The comment's user author ID actorType?: ActorTypeEnum; resolved?: boolean; resolvedAt?: Date; - resolvedBy?: IUser; - resolvedById?: ID; editedAt?: Date; members?: IEmployee[]; // Indicates members assigned to comment teams?: IOrganizationTeam[]; // Indicates teams assigned to comment parent?: IComment; parentId?: ID; // Specify the parent comment if current one is a reply replies?: IComment[]; + resolvedByEmployee?: IEmployee; // Indicates the employee who resolved the comment + resolvedByEmployeeId?: ID; // Indicates the employee ID who resolved the comment } -export interface ICommentCreateInput extends IBasePerEntityType, IMentionUserIds { +export interface ICommentCreateInput extends IBasePerEntityType, ICommentAuthor, IMentionEmployeeIds { comment: string; entityName?: string; - parentId?: ID; + parent?: IComment; + parentId?: ID; // Specify the parent comment if current one is a reply members?: IEmployee[]; teams?: IOrganizationTeam[]; } export interface ICommentUpdateInput - extends IMentionUserIds, - Partial> { } + extends IMentionEmployeeIds, + Partial> {} -export interface ICommentFindInput extends Pick { } +export interface ICommentFindInput extends Pick {} diff --git a/packages/contracts/src/lib/mention.model.ts b/packages/contracts/src/lib/mention.model.ts index 6785a38db48..269bc6af623 100644 --- a/packages/contracts/src/lib/mention.model.ts +++ b/packages/contracts/src/lib/mention.model.ts @@ -14,6 +14,6 @@ export interface IMentionCreateInput extends Omit { entityName?: string; } -export interface IMentionUserIds { - mentionUserIds?: ID[]; +export interface IMentionEmployeeIds { + mentionEmployeeIds?: ID[]; } diff --git a/packages/contracts/src/lib/screening-task.model.ts b/packages/contracts/src/lib/screening-task.model.ts index 6361cbedc93..52f5d07ad21 100644 --- a/packages/contracts/src/lib/screening-task.model.ts +++ b/packages/contracts/src/lib/screening-task.model.ts @@ -1,5 +1,5 @@ import { IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; -import { IMentionUserIds } from './mention.model'; +import { IMentionEmployeeIds } from './mention.model'; import { ITask } from './task.model'; import { IUser } from './user.model'; @@ -20,6 +20,6 @@ export enum ScreeningTaskStatusEnum { PENDING = 'pending' } -export interface IScreeningTaskCreateInput extends Omit, IMentionUserIds {} +export interface IScreeningTaskCreateInput extends Omit, IMentionEmployeeIds {} export interface IScreeningTaskUpdateInput extends Omit {} diff --git a/packages/contracts/src/lib/task.model.ts b/packages/contracts/src/lib/task.model.ts index b0f5534d0d4..3c9777a8d3c 100644 --- a/packages/contracts/src/lib/task.model.ts +++ b/packages/contracts/src/lib/task.model.ts @@ -15,7 +15,7 @@ import { ITaskPriority, TaskPriorityEnum } from './task-priority.model'; import { ITaskSize, TaskSizeEnum } from './task-size.model'; import { IOrganizationProjectModule } from './organization-project-module.model'; import { IIssueType, TaskTypeEnum } from './issue-type.model'; -import { IMentionUserIds } from './mention.model'; +import { IMentionEmployeeIds } from './mention.model'; export interface ITask extends IBasePerTenantAndOrganizationEntityModel, @@ -80,7 +80,7 @@ export enum TaskParticipantEnum { TEAMS = 'teams' } -export interface ITaskCreateInput extends ITask, IMentionUserIds {} +export interface ITaskCreateInput extends ITask, IMentionEmployeeIds {} export interface ITaskUpdateInput extends ITaskCreateInput { taskSprintMoveReason?: string; diff --git a/packages/contracts/src/lib/user.model.ts b/packages/contracts/src/lib/user.model.ts index eb47233acf4..bdd16b76dd2 100644 --- a/packages/contracts/src/lib/user.model.ts +++ b/packages/contracts/src/lib/user.model.ts @@ -36,7 +36,7 @@ export type ExcludeCreatorFields = Omit; * Interface representing a relationship with a creator. */ export interface IHasCreator { - creator?: IUser; + creator?: IUser; // TODO : Replace these properties with `createdBy` and `createdById` creatorId?: ID; } diff --git a/packages/core/src/lib/comment/commands/handlers/comment.create.handler.ts b/packages/core/src/lib/comment/commands/handlers/comment.create.handler.ts index e75a595c7d3..1bfae624e92 100644 --- a/packages/core/src/lib/comment/commands/handlers/comment.create.handler.ts +++ b/packages/core/src/lib/comment/commands/handlers/comment.create.handler.ts @@ -1,3 +1,4 @@ +import { BadRequestException } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { IComment } from '@gauzy/contracts'; import { CommentService } from '../../comment.service'; @@ -5,10 +6,26 @@ import { CommentCreateCommand } from '../comment.create.command'; @CommandHandler(CommentCreateCommand) export class CommentCreateHandler implements ICommandHandler { - constructor(private readonly commentService: CommentService) {} + constructor(readonly commentService: CommentService) {} + /** + * Executes the CommentCreateCommand to create a new comment. + * + * This function extracts the input data from the provided command and invokes the comment service + * to create a comment. If an error occurs during the process, it logs the error and throws a + * BadRequestException. + * + * @param {CommentCreateCommand} command - The command containing the input data for creating the comment. + * @returns {Promise} A promise that resolves to the newly created comment. + * @throws {BadRequestException} If the comment creation fails. + */ public async execute(command: CommentCreateCommand): Promise { - const { input } = command; - return await this.commentService.create(input); + try { + const { input } = command; + return await this.commentService.create(input); + } catch (error) { + console.log(`Error while creating comment: ${error.message}`, error); + throw new BadRequestException('Comment post failed', error); + } } } diff --git a/packages/core/src/lib/comment/commands/handlers/comment.update.handler.ts b/packages/core/src/lib/comment/commands/handlers/comment.update.handler.ts index 464bd7f617f..6dc1eb47923 100644 --- a/packages/core/src/lib/comment/commands/handlers/comment.update.handler.ts +++ b/packages/core/src/lib/comment/commands/handlers/comment.update.handler.ts @@ -1,15 +1,33 @@ +import { BadRequestException } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { UpdateResult } from 'typeorm'; +import { IComment } from '@gauzy/contracts'; import { CommentService } from '../../comment.service'; import { CommentUpdateCommand } from '../comment.update.command'; -import { IComment } from '@gauzy/contracts'; @CommandHandler(CommentUpdateCommand) export class CommentUpdateHandler implements ICommandHandler { - constructor(private readonly commentService: CommentService) {} - + constructor(readonly commentService: CommentService) {} + /** + * Executes the CommentUpdateCommand to update an existing comment. + * + * This function extracts the comment ID and the update input from the command, and then invokes the + * comment service's update method to perform the update. It returns a promise that resolves to either + * the updated comment or an update result. + * + * If an error occurs during the update, the error is logged and a BadRequestException is thrown. + * + * @param {CommentUpdateCommand} command - The command containing the comment ID and update data. + * @returns {Promise} A promise that resolves to the updated comment or update result. + * @throws {BadRequestException} If the update process fails. + */ public async execute(command: CommentUpdateCommand): Promise { - const { id, input } = command; - return await this.commentService.update(id, input); + try { + const { id, input } = command; + return await this.commentService.update(id, input); + } catch (error) { + console.log(`Error while updating comment: ${error.message}`, error); + throw new BadRequestException('Comment update failed', error); + } } } diff --git a/packages/core/src/lib/comment/comment.controller.ts b/packages/core/src/lib/comment/comment.controller.ts index dc9c5297772..42259c44d3f 100644 --- a/packages/core/src/lib/comment/comment.controller.ts +++ b/packages/core/src/lib/comment/comment.controller.ts @@ -15,9 +15,9 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { CommandBus } from '@nestjs/cqrs'; import { DeleteResult } from 'typeorm'; import { IComment, ID, IPagination } from '@gauzy/contracts'; -import { UUIDValidationPipe, UseValidationPipe } from './../shared/pipes'; +import { CrudController, OptionParams, PaginationParams } from '../core/crud'; +import { UUIDValidationPipe, UseValidationPipe } from '../shared/pipes'; import { PermissionGuard, TenantPermissionGuard } from '../shared/guards'; -import { CrudController, OptionParams, PaginationParams } from './../core/crud'; import { Comment } from './comment.entity'; import { CommentService } from './comment.service'; import { CommentCreateCommand, CommentUpdateCommand } from './commands'; @@ -31,9 +31,13 @@ export class CommentController extends CrudController { super(commentService); } - @ApiOperation({ - summary: 'Find all comments filtered by type.' - }) + /** + * Finds all comments filtered by type (or other criteria) with pagination. + * + * @param params - Pagination and filter parameters. + * @returns A promise that resolves with paginated comments. + */ + @ApiOperation({ summary: 'Find all comments filtered by type.' }) @ApiResponse({ status: HttpStatus.OK, description: 'Found comments', @@ -43,26 +47,39 @@ export class CommentController extends CrudController { status: HttpStatus.NOT_FOUND, description: 'Record not found' }) - @Get() + @Get('/') @UseValidationPipe() async findAll(@Query() params: PaginationParams): Promise> { return await this.commentService.findAll(params); } + /** + * Finds a comment by its id. + * + * @param id - The id of the comment. + * @param params - Optional parameters (e.g., relations to load). + * @returns The found comment record. + */ @ApiOperation({ summary: 'Find by id' }) @ApiResponse({ status: HttpStatus.OK, - description: 'Found one record' /*, type: T*/ + description: 'Found one record' }) @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Record not found' }) - @Get(':id') + @Get('/:id') async findById(@Param('id', UUIDValidationPipe) id: ID, @Query() params: OptionParams): Promise { return this.commentService.findOneByIdString(id, params); } + /** + * Creates a new comment using the provided DTO. + * + * @param createCommentDto - Data transfer object containing comment data. + * @returns A promise resolving to the created comment. + */ @ApiOperation({ summary: 'Create/Post a comment' }) @ApiResponse({ status: HttpStatus.CREATED, @@ -72,13 +89,21 @@ export class CommentController extends CrudController { status: HttpStatus.BAD_REQUEST, description: 'Invalid input, The response body may contain clues as to what went wrong' }) - @HttpCode(HttpStatus.ACCEPTED) - @Post() + @HttpCode(HttpStatus.CREATED) + @Post('/') @UseValidationPipe({ whitelist: true }) async create(@Body() entity: CreateCommentDTO): Promise { return await this.commandBus.execute(new CommentCreateCommand(entity)); } + /** + * Updates an existing comment identified by the provided id. + * + * @param id - The unique identifier of the comment. + * @param updateCommentDto - The data transfer object containing update data. + * @returns The updated comment. + * @throws NotFoundException if the comment does not exist. + */ @ApiOperation({ summary: 'Update an existing comment' }) @ApiResponse({ status: HttpStatus.CREATED, @@ -93,12 +118,19 @@ export class CommentController extends CrudController { description: 'Invalid input, The response body may contain clues as to what went wrong' }) @HttpCode(HttpStatus.ACCEPTED) - @Put(':id') + @Put('/:id') @UseValidationPipe({ whitelist: true }) async update(@Param('id', UUIDValidationPipe) id: ID, @Body() entity: UpdateCommentDTO): Promise { return await this.commandBus.execute(new CommentUpdateCommand(id, entity)); } + /** + * Deletes a comment identified by the given id. + * + * @param id - The unique identifier of the comment to delete. + * @returns A promise resolving to the result of the delete operation. + * @throws NotFoundException if the comment is not found. + */ @ApiOperation({ summary: 'Delete comment' }) @ApiResponse({ status: HttpStatus.NO_CONTENT, diff --git a/packages/core/src/lib/comment/comment.entity.ts b/packages/core/src/lib/comment/comment.entity.ts index 3c58ebe7a8d..ad7e2b795a1 100644 --- a/packages/core/src/lib/comment/comment.entity.ts +++ b/packages/core/src/lib/comment/comment.entity.ts @@ -2,10 +2,10 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; // eslint-disable-next-line @typescript-eslint/no-unused-vars 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 { IsBoolean, IsDate, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator'; import { Type } from 'class-transformer'; -import { ActorTypeEnum, IComment, ID, IEmployee, IOrganizationTeam, IUser } from '@gauzy/contracts'; -import { BasePerEntityType, Employee, OrganizationTeam, User } from '../core/entities/internal'; +import { ActorTypeEnum, IComment, ID, IEmployee, IOrganizationTeam } from '@gauzy/contracts'; +import { BasePerEntityType, Employee, OrganizationTeam } from '../core/entities/internal'; import { ColumnIndex, MultiORMColumn, @@ -59,13 +59,10 @@ export class Comment extends BasePerEntityType implements IComment { | @ManyToOne |-------------------------------------------------------------------------- */ - /** - * User comment author + * Comment author */ - @ApiPropertyOptional({ type: () => Object }) - @IsOptional() - @MultiORMManyToOne(() => User, { + @MultiORMManyToOne(() => Employee, { /** Indicates if relation column value can be nullable or not. */ nullable: true, @@ -73,22 +70,23 @@ export class Comment extends BasePerEntityType implements IComment { onDelete: 'CASCADE' }) @JoinColumn() - creator?: IUser; + employee?: IEmployee; + /** + * Comment author ID + */ @ApiPropertyOptional({ type: () => String }) @IsOptional() @IsUUID() - @RelationId((it: Comment) => it.creator) + @RelationId((it: Comment) => it.employee) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - creatorId?: ID; + employeeId?: ID; /** - * User marked comment as resolved + * Resolved by */ - @ApiPropertyOptional({ type: () => Object }) - @IsOptional() - @MultiORMManyToOne(() => User, { + @MultiORMManyToOne(() => Employee, { /** Indicates if relation column value can be nullable or not. */ nullable: true, @@ -96,26 +94,35 @@ export class Comment extends BasePerEntityType implements IComment { onDelete: 'CASCADE' }) @JoinColumn() - resolvedBy?: IUser; + resolvedByEmployee?: IEmployee; + /** + * Resolved by ID + */ @ApiPropertyOptional({ type: () => String }) @IsOptional() @IsUUID() - @RelationId((it: Comment) => it.resolvedBy) + @RelationId((it: Comment) => it.resolvedByEmployee) @ColumnIndex() @MultiORMColumn({ nullable: true, relationId: true }) - resolvedById?: ID; + resolvedByEmployeeId?: ID; /** * Comment parent-child relationship */ - @ApiPropertyOptional({ type: () => Comment }) - @IsOptional() @MultiORMManyToOne(() => Comment, (comment) => comment.replies, { + /** Indicates if relation column value can be nullable or not. */ + nullable: true, + + /** Database cascade action on delete. */ onDelete: 'SET NULL' }) + @JoinColumn() parent?: IComment; + /** + * Parent ID of comment + */ @ApiPropertyOptional({ type: () => String }) @IsOptional() @IsUUID() @@ -128,7 +135,6 @@ export class Comment extends BasePerEntityType implements IComment { | @OneToMany |-------------------------------------------------------------------------- */ - /** * Replies comments */ @@ -141,40 +147,30 @@ export class Comment extends BasePerEntityType implements IComment { |-------------------------------------------------------------------------- */ /** - * Members + * Comment members */ - @ApiPropertyOptional({ type: () => Array, isArray: true }) - @IsOptional() - @IsArray() @MultiORMManyToMany(() => Employee, (employee) => employee.assignedComments, { - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - owner: true, - pivotTable: 'comment_employee', - joinColumn: 'commentId', - inverseJoinColumn: 'employeeId' - }) - @JoinTable({ - name: 'comment_employee' + onUpdate: 'CASCADE', // Cascade action on update + onDelete: 'CASCADE', // Cascade action on delete + owner: true, // Ownership + pivotTable: 'comment_employee', // Table name for pivot table + joinColumn: 'commentId', // Column name for join table + inverseJoinColumn: 'employeeId' // Column name for inverse join table }) + @JoinTable({ name: 'comment_employee' }) members?: IEmployee[]; /** - * OrganizationTeam + * Comment teams */ - @ApiPropertyOptional({ type: () => Array, isArray: true }) - @IsOptional() - @IsArray() @MultiORMManyToMany(() => OrganizationTeam, (team) => team.assignedComments, { - onUpdate: 'CASCADE', - onDelete: 'CASCADE', - owner: true, - pivotTable: 'comment_team', - joinColumn: 'commentId', - inverseJoinColumn: 'organizationTeamId' - }) - @JoinTable({ - name: 'comment_team' + onUpdate: 'CASCADE', // Cascade action on update + onDelete: 'CASCADE', // Cascade action on delete + owner: true, // Ownership + pivotTable: 'comment_team', // Table name for pivot table + joinColumn: 'commentId', // Column name for join table + inverseJoinColumn: 'organizationTeamId' // Column name for inverse join table }) + @JoinTable({ name: 'comment_team' }) teams?: IOrganizationTeam[]; } diff --git a/packages/core/src/lib/comment/comment.module.ts b/packages/core/src/lib/comment/comment.module.ts index a7253672c0f..b46865b288a 100644 --- a/packages/core/src/lib/comment/comment.module.ts +++ b/packages/core/src/lib/comment/comment.module.ts @@ -4,7 +4,7 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { MentionModule } from '../mention/mention.module'; import { RolePermissionModule } from '../role-permission/role-permission.module'; -import { UserModule } from '../user/user.module'; +import { EmployeeModule } from '../employee/employee.module'; import { CommandHandlers } from './commands/handlers'; import { CommentService } from './comment.service'; import { CommentController } from './comment.controller'; @@ -16,7 +16,7 @@ import { TypeOrmCommentRepository } from './repository/type-orm.comment.reposito TypeOrmModule.forFeature([Comment]), MikroOrmModule.forFeature([Comment]), RolePermissionModule, - UserModule, + EmployeeModule, MentionModule, CqrsModule ], diff --git a/packages/core/src/lib/comment/comment.service.ts b/packages/core/src/lib/comment/comment.service.ts index f242ab9d63c..2430c65ffab 100644 --- a/packages/core/src/lib/comment/comment.service.ts +++ b/packages/core/src/lib/comment/comment.service.ts @@ -1,8 +1,6 @@ import { EventBus } from '@nestjs/cqrs'; import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { UpdateResult } from 'typeorm'; -import { TenantAwareCrudService } from './../core/crud'; -import { RequestContext } from '../core/context'; import { BaseEntityEnum, IComment, @@ -11,8 +9,10 @@ import { ID, SubscriptionTypeEnum } from '@gauzy/contracts'; +import { TenantAwareCrudService } from './../core/crud'; +import { RequestContext } from '../core/context'; import { CreateSubscriptionEvent } from '../subscription/events'; -import { UserService } from '../user/user.service'; +import { EmployeeService } from '../employee/employee.service'; import { MentionService } from '../mention/mention.service'; import { Comment } from './comment.entity'; import { TypeOrmCommentRepository } from './repository/type-orm.comment.repository'; @@ -24,114 +24,129 @@ export class CommentService extends TenantAwareCrudService { readonly typeOrmCommentRepository: TypeOrmCommentRepository, readonly mikroOrmCommentRepository: MikroOrmCommentRepository, private readonly _eventBus: EventBus, - private readonly userService: UserService, - private readonly mentionService: MentionService + private readonly _employeeService: EmployeeService, + private readonly _mentionService: MentionService ) { super(typeOrmCommentRepository, mikroOrmCommentRepository); } /** - * @description Create / Post comment - Note - * @param {ICommentCreateInput} input - Data to creating comment - * @returns A promise that resolves to the created comment - * @memberof CommentService + * Creates a new comment with the provided input, handling employee validation, + * publishing mention notifications, and subscribing the comment creator to the related entity. + * + * This function retrieves context-specific IDs from the RequestContext (tenant, employee) + * and falls back to the values in the input if necessary. It verifies that the employee exists, + * creates the comment, publishes mention notifications for each mentioned employee, and + * triggers a subscription event for the creator. + * + * @param {ICommentCreateInput} input - The input data required to create a comment, including text, mentions, and organization details. + * @returns {Promise} A promise that resolves to the newly created comment. + * @throws {NotFoundException} If the employee associated with the comment is not found. + * @throws {BadRequestException} If any error occurs during the creation of the comment. */ async create(input: ICommentCreateInput): Promise { try { - const userId = RequestContext.currentUserId(); - const tenantId = RequestContext.currentTenantId(); - const { mentionUserIds = [], ...data } = input; + // Retrieve context-specific IDs. + const tenantId = RequestContext.currentTenantId() ?? input.tenantId; + const employeeId = RequestContext.currentEmployeeId() ?? input.employeeId; + const { mentionEmployeeIds = [], organizationId, ...data } = input; - // Employee existence validation - const user = await this.userService.findOneByIdString(userId); - if (!user) { - throw new NotFoundException('User not found'); + // Validate that the employee exists. + const employee = await this._employeeService.findOneByIdString(employeeId); + if (!employee) { + throw new NotFoundException(`Employee with id ${employeeId} not found`); } - // create comment + // Create the comment. const comment = await super.create({ ...data, + employeeId, tenantId, - creatorId: user.id + organizationId }); - // Apply mentions if needed + // Publish mentions for each mentioned employee, if any. await Promise.all( - mentionUserIds.map((mentionedUserId) => - this.mentionService.publishMention({ + mentionEmployeeIds.map((mentionedUserId) => + this._mentionService.publishMention({ entity: BaseEntityEnum.Comment, entityId: comment.id, + entityName: input.entityName, mentionedUserId, - mentionById: user.id, + mentionById: employee.id, parentEntityId: comment.entityId, parentEntityType: comment.entity, organizationId: comment.organizationId, - tenantId, - entityName: input.entityName + tenantId: comment.tenantId }) ) ); - // Subscribe creator to the entity + // Subscribe the comment creator to the entity. this._eventBus.publish( new CreateSubscriptionEvent({ entity: input.entity, entityId: input.entityId, - userId: user.id, type: SubscriptionTypeEnum.COMMENT, organizationId: comment.organizationId, - tenantId + tenantId: comment.tenantId }) ); - // Return created Comment + // Return the newly created comment. return comment; } catch (error) { - console.log(error); // Debug Logging + console.log(`Error while creating comment: ${error.message}`, error); throw new BadRequestException('Comment post failed', error); } } /** - * @description Update comment - Note - * @param id - The comment ID to be updated. - * @param {ICommentUpdateInput} input - Data to update comment. - * @returns A promise that resolves to the updated comment OR Update result. - * @memberof CommentService + * Updates an existing comment based on the provided id and update input. + * + * This function first retrieves the current employee's ID from the request context, + * then attempts to locate the comment matching the provided id and employeeId. + * If the comment is found, it creates an updated version of the comment using the input data. + * Additionally, it synchronizes any mention updates via the _mentionService. + * + * @param {ID} id - The unique identifier of the comment to update. + * @param {ICommentUpdateInput} input - The update data for the comment, including any mention updates. + * @returns {Promise} A promise that resolves to the updated comment or an update result. + * @throws {BadRequestException} If the comment is not found or if the update operation fails. */ async update(id: ID, input: ICommentUpdateInput): Promise { try { - const { mentionUserIds = [] } = input; + const employeeId = RequestContext.currentEmployeeId(); + const { mentionEmployeeIds = [] } = input; - const userId = RequestContext.currentUserId(); - const comment = await this.findOneByOptions({ - where: { - id, - creatorId: userId - } + // Find the comment for the current employee with the given id. + const comment = await this.findOneByWhereOptions({ + id, + employeeId }); if (!comment) { - throw new BadRequestException('Comment not found'); + throw new BadRequestException(`Comment with id ${id} not found`); } + // Update the comment with the new input data. const updatedComment = await super.create({ ...input, id }); - // Synchronize mentions - await this.mentionService.updateEntityMentions( + // Synchronize any mention updates for the comment. + await this._mentionService.updateEntityMentions( BaseEntityEnum.Comment, id, - mentionUserIds, + mentionEmployeeIds, updatedComment.entityId, updatedComment.entity ); return updatedComment; } catch (error) { - console.log(error); // Debug Logging + console.log(`Error while updating comment: ${error.message}`, error); throw new BadRequestException('Comment update failed', error); } } diff --git a/packages/core/src/lib/comment/dto/create-comment.dto.ts b/packages/core/src/lib/comment/dto/create-comment.dto.ts index f805f8b3c99..a4fd103ac17 100644 --- a/packages/core/src/lib/comment/dto/create-comment.dto.ts +++ b/packages/core/src/lib/comment/dto/create-comment.dto.ts @@ -1,18 +1,15 @@ -import { ApiPropertyOptional, IntersectionType, OmitType } from '@nestjs/swagger'; +import { ApiPropertyOptional, IntersectionType } from '@nestjs/swagger'; import { IsOptional, IsString } from 'class-validator'; import { ICommentCreateInput } from '@gauzy/contracts'; import { TenantOrganizationBaseDTO } from './../../core/dto'; -import { MentionUserIdsDTO } from '../../mention/dto'; +import { MentionEmployeeIdsDTO } from '../../mention/dto'; import { Comment } from '../comment.entity'; /** * Create Comment data validation request DTO */ export class CreateCommentDTO - extends IntersectionType( - TenantOrganizationBaseDTO, - IntersectionType(OmitType(Comment, ['creatorId', 'creator']), MentionUserIdsDTO) - ) + extends IntersectionType(TenantOrganizationBaseDTO, Comment, MentionEmployeeIdsDTO) implements ICommentCreateInput { @ApiPropertyOptional({ type: () => String }) diff --git a/packages/core/src/lib/database/migrations/1739364133493-AlterCommentEntityTable.ts b/packages/core/src/lib/database/migrations/1739364133493-AlterCommentEntityTable.ts new file mode 100644 index 00000000000..05402069c53 --- /dev/null +++ b/packages/core/src/lib/database/migrations/1739364133493-AlterCommentEntityTable.ts @@ -0,0 +1,767 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import * as chalk from 'chalk'; +import { DatabaseTypeEnum } from '@gauzy/config'; + +export class AlterCommentEntityTable1739364133493 implements MigrationInterface { + name = 'AlterCommentEntityTable1739364133493'; + + /** + * Up Migration + * + * @param queryRunner + */ + public async up(queryRunner: QueryRunner): Promise { + console.log(chalk.yellow(this.name + ' start running!')); + + switch (queryRunner.connection.options.type) { + case DatabaseTypeEnum.sqlite: + case DatabaseTypeEnum.betterSqlite3: + await this.sqliteUpQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.postgres: + await this.postgresUpQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.mysql: + await this.mysqlUpQueryRunner(queryRunner); + break; + default: + throw Error(`Unsupported database: ${queryRunner.connection.options.type}`); + } + } + + /** + * Down Migration + * + * @param queryRunner + */ + public async down(queryRunner: QueryRunner): Promise { + switch (queryRunner.connection.options.type) { + case DatabaseTypeEnum.sqlite: + case DatabaseTypeEnum.betterSqlite3: + await this.sqliteDownQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.postgres: + await this.postgresDownQueryRunner(queryRunner); + break; + case DatabaseTypeEnum.mysql: + await this.mysqlDownQueryRunner(queryRunner); + break; + default: + throw Error(`Unsupported database: ${queryRunner.connection.options.type}`); + } + } + + /** + * PostgresDB Up Migration + * + * @param queryRunner + */ + public async postgresUpQueryRunner(queryRunner: QueryRunner): Promise { + // Step 1: Drop existing foreign keys on "creatorId" and "resolvedById" + console.log('Step 1: Dropping existing foreign keys...'); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f"`); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_c9409c81aa283c1aae70fd5f4c3"`); + + // Step 2: Drop existing indexes on "creatorId" and "resolvedById" + console.log('Step 2: Dropping existing indexes on "creatorId" and "resolvedById"...'); + await queryRunner.query(`DROP INDEX "public"."IDX_b6bf60ecb9f6c398e349adff52"`); + await queryRunner.query(`DROP INDEX "public"."IDX_c9409c81aa283c1aae70fd5f4c"`); + + // Step 3: Add new columns "employeeId" and "resolvedByEmployeeId" + console.log('Step 3: Adding new columns "employeeId" and "resolvedByEmployeeId"...'); + await queryRunner.query(`ALTER TABLE "comment" ADD "employeeId" uuid`); + await queryRunner.query(`ALTER TABLE "comment" ADD "resolvedByEmployeeId" uuid`); + + // Step 4: Copy data from "creatorId" to "employeeId" using the employee mapping + console.log('Step 4: Copying data from "creatorId" to "employeeId" via employee table mapping...'); + await queryRunner.query(` + UPDATE "comment" + SET "employeeId" = e.id + FROM "employee" e + WHERE "comment"."creatorId" = e."userId" AND "comment"."creatorId" IS NOT NULL + `); + + // Step 5: Copy data from "resolvedById" to "resolvedByEmployeeId" using the employee mapping + console.log('Step 5: Copying data from "resolvedById" to "resolvedByEmployeeId" via employee table mapping...'); + await queryRunner.query(` + UPDATE "comment" + SET "resolvedByEmployeeId" = e.id + FROM "employee" e + WHERE "comment"."resolvedById" = e."userId" AND "comment"."resolvedById" IS NOT NULL + `); + + // Step 6: Drop the old columns "creatorId" and "resolvedById" + console.log('Step 6: Dropping old columns "creatorId" and "resolvedById"...'); + await queryRunner.query(`ALTER TABLE "comment" DROP COLUMN "creatorId"`); + await queryRunner.query(`ALTER TABLE "comment" DROP COLUMN "resolvedById"`); + + // Step 7: Create indexes on the new columns "employeeId" and "resolvedByEmployeeId" + console.log('Step 7: Creating indexes on "employeeId" and "resolvedByEmployeeId"...'); + await queryRunner.query(`CREATE INDEX "IDX_7a88834dadfa6fe261268bfcee" ON "comment" ("employeeId")`); + await queryRunner.query(`CREATE INDEX "IDX_35cddb3e66a46587966b68a921" ON "comment" ("resolvedByEmployeeId")`); + + // Step 8: Add foreign key constraint for "employeeId" + console.log('Step 8: Adding foreign key constraint for "employeeId"...'); + await queryRunner.query(` + ALTER TABLE "comment" + ADD CONSTRAINT "FK_7a88834dadfa6fe261268bfceef" + FOREIGN KEY ("employeeId") REFERENCES "employee"("id") ON DELETE CASCADE ON UPDATE NO ACTION + `); + + // Step 9: Add foreign key constraint for "resolvedByEmployeeId" + console.log('Step 9: Adding foreign key constraint for "resolvedByEmployeeId"...'); + await queryRunner.query(` + ALTER TABLE "comment" + ADD CONSTRAINT "FK_35cddb3e66a46587966b68a9217" + FOREIGN KEY ("resolvedByEmployeeId") REFERENCES "employee"("id") ON DELETE CASCADE ON UPDATE NO ACTION + `); + } + + /** + * PostgresDB Down Migration + * + * @param queryRunner + */ + public async postgresDownQueryRunner(queryRunner: QueryRunner): Promise { + // Step 1: Drop foreign key constraints for "resolvedByEmployeeId" and "employeeId" + console.log('Step 1: Dropping foreign key constraints for "resolvedByEmployeeId" and "employeeId"...'); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_35cddb3e66a46587966b68a9217"`); + await queryRunner.query(`ALTER TABLE "comment" DROP CONSTRAINT "FK_7a88834dadfa6fe261268bfceef"`); + + // Step 2: Drop indexes on "resolvedByEmployeeId" and "employeeId" + console.log('Step 2: Dropping indexes on "resolvedByEmployeeId" and "employeeId"...'); + await queryRunner.query(`DROP INDEX "public"."IDX_35cddb3e66a46587966b68a921"`); + await queryRunner.query(`DROP INDEX "public"."IDX_7a88834dadfa6fe261268bfcee"`); + + // Step 3: Re-add the old columns "resolvedById" and "creatorId" + console.log('Step 3: Adding columns "resolvedById" and "creatorId"...'); + await queryRunner.query(`ALTER TABLE "comment" ADD "resolvedById" uuid`); + await queryRunner.query(`ALTER TABLE "comment" ADD "creatorId" uuid`); + + // Step 4: Copy data from "employeeId" to "creatorId" via employee table mapping + console.log('Step 4: Copying data from "employeeId" to "creatorId" via employee table mapping...'); + await queryRunner.query(` + UPDATE "comment" + SET "creatorId" = e."userId" + FROM "employee" e + WHERE "comment"."employeeId" = e.id AND "comment"."employeeId" IS NOT NULL + `); + + // Step 5: Copy data from "resolvedByEmployeeId" to "resolvedById" via employee table mapping + console.log('Step 5: Copying data from "resolvedByEmployeeId" to "resolvedById" via employee table mapping...'); + await queryRunner.query(` + UPDATE "comment" + SET "resolvedById" = e."userId" + FROM "employee" e + WHERE "comment"."resolvedByEmployeeId" = e.id AND "comment"."resolvedByEmployeeId" IS NOT NULL + `); + + // Step 6: Drop the new columns "resolvedByEmployeeId" and "employeeId" + console.log('Step 6: Dropping columns "resolvedByEmployeeId" and "employeeId"...'); + await queryRunner.query(`ALTER TABLE "comment" DROP COLUMN "resolvedByEmployeeId"`); + await queryRunner.query(`ALTER TABLE "comment" DROP COLUMN "employeeId"`); + + // Step 7: Create indexes on the restored columns "resolvedById" and "creatorId" + console.log('Step 7: Creating indexes on "resolvedById" and "creatorId"...'); + await queryRunner.query(`CREATE INDEX "IDX_c9409c81aa283c1aae70fd5f4c" ON "comment" ("resolvedById")`); + await queryRunner.query(`CREATE INDEX "IDX_b6bf60ecb9f6c398e349adff52" ON "comment" ("creatorId")`); + + // Step 8: Add foreign key constraint for "resolvedById" referencing "user" + console.log('Step 8: Adding foreign key constraint for "resolvedById" referencing "user"...'); + await queryRunner.query(` + ALTER TABLE "comment" ADD CONSTRAINT "FK_c9409c81aa283c1aae70fd5f4c3" + FOREIGN KEY ("resolvedById") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION + `); + + // Step 9: Add foreign key constraint for "creatorId" referencing "user" + console.log('Step 9: Adding foreign key constraint for "creatorId" referencing "user"...'); + await queryRunner.query(` + ALTER TABLE "comment" ADD CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f" + FOREIGN KEY ("creatorId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION + `); + } + + /** + * SqliteDB and BetterSQlite3DB Up Migration + * + * @param queryRunner + */ + public async sqliteUpQueryRunner(queryRunner: QueryRunner): Promise { + // Step 1: Drop existing indexes from the current "comment" table. + console.log('Step 1: Dropping existing indexes from "comment"...'); + await queryRunner.query(`DROP INDEX "IDX_3620aeff4ac5c977176226017e"`); + await queryRunner.query(`DROP INDEX "IDX_da3cd25ed3a6ce76770f00c3da"`); + await queryRunner.query(`DROP INDEX "IDX_8f58834bed39f0f9e85f048eaf"`); + await queryRunner.query(`DROP INDEX "IDX_a3422826753d4e6b079dea9834"`); + await queryRunner.query(`DROP INDEX "IDX_097e339f6cb990306d19880a4c"`); + await queryRunner.query(`DROP INDEX "IDX_2950cfa146fc50334efa61a70b"`); + await queryRunner.query(`DROP INDEX "IDX_eecd6e41f9acb6bf59e474d518"`); + await queryRunner.query(`DROP INDEX "IDX_b6bf60ecb9f6c398e349adff52"`); + await queryRunner.query(`DROP INDEX "IDX_c9409c81aa283c1aae70fd5f4c"`); + await queryRunner.query(`DROP INDEX "IDX_e3aebe2bd1c53467a07109be59"`); + + // Step 2: Create a temporary table with extra columns. + console.log('Step 2: Creating temporary table "temporary_comment" with new columns...'); + await queryRunner.query(` + CREATE TABLE "temporary_comment" ( + "deletedAt" datetime, + "id" varchar PRIMARY KEY NOT NULL, + "createdAt" datetime NOT NULL DEFAULT (datetime('now')), + "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), + "isActive" boolean DEFAULT (1), + "isArchived" boolean DEFAULT (0), + "archivedAt" datetime, + "tenantId" varchar, + "organizationId" varchar, + "entity" varchar NOT NULL, + "entityId" varchar NOT NULL, + "comment" text NOT NULL, + "actorType" integer, + "resolved" boolean, + "resolvedAt" datetime, + "editedAt" datetime, + "parentId" varchar, + "creatorId" varchar, + "resolvedById" varchar, + "employeeId" varchar, + "resolvedByEmployeeId" varchar, + CONSTRAINT "FK_8f58834bed39f0f9e85f048eafe" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_a3422826753d4e6b079dea98342" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "FK_e3aebe2bd1c53467a07109be596" FOREIGN KEY ("parentId") REFERENCES "comment" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, + CONSTRAINT "FK_7a88834dadfa6fe261268bfceef" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_35cddb3e66a46587966b68a9217" FOREIGN KEY ("resolvedByEmployeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `); + + // Step 3: Copy data from the old "comment" table into the temporary table. + console.log('Step 3: Copying data from the existing "comment" table into "temporary_comment"...'); + await queryRunner.query(` + INSERT INTO "temporary_comment" ( + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "parentId", + "creatorId", + "resolvedById" + ) + SELECT + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "parentId", + "creatorId", + "resolvedById" + FROM "comment" + `); + + // Step 4: Copy data from "creatorId" to "employeeId" using the employee mapping + console.log('Step 4: Updating "employeeId" from "creatorId" via employee table mapping...'); + await queryRunner.query(` + UPDATE "temporary_comment" + SET "employeeId" = ( + SELECT e."id" + FROM "employee" e + WHERE e."userId" = "temporary_comment"."creatorId" + LIMIT 1 + ) + WHERE "creatorId" IS NOT NULL + `); + + // Step 5: Copy data from "resolvedById" to "resolvedByEmployeeId" using the employee mapping + console.log('Step 5: Updating "resolvedByEmployeeId" from "resolvedById" via employee table mapping...'); + await queryRunner.query(` + UPDATE "temporary_comment" + SET "resolvedByEmployeeId" = ( + SELECT e."id" + FROM "employee" e + WHERE e."userId" = "temporary_comment"."resolvedById" + LIMIT 1 + ) + WHERE "resolvedById" IS NOT NULL + `); + + // Step 6: Drop the old "comment" table and rename the temporary table to it. + console.log('Step 6: Dropping the old "comment" table and renaming the temporary table to it...'); + await queryRunner.query(`DROP TABLE "comment"`); + await queryRunner.query(`ALTER TABLE "temporary_comment" RENAME TO "comment"`); + + // Step 7: Recreate the "temporary_comment" table without the old columns. + console.log('Step 7: Recreating "temporary_comment" table without old columns...'); + await queryRunner.query(` + CREATE TABLE "temporary_comment" ( + "deletedAt" datetime, + "id" varchar PRIMARY KEY NOT NULL, + "createdAt" datetime NOT NULL DEFAULT (datetime('now')), + "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), + "isActive" boolean DEFAULT (1), + "isArchived" boolean DEFAULT (0), + "archivedAt" datetime, + "tenantId" varchar, + "organizationId" varchar, + "entity" varchar NOT NULL, + "entityId" varchar NOT NULL, + "comment" text NOT NULL, + "actorType" integer, + "resolved" boolean, + "resolvedAt" datetime, + "editedAt" datetime, + "parentId" varchar, + "employeeId" varchar, + "resolvedByEmployeeId" varchar, + CONSTRAINT "FK_8f58834bed39f0f9e85f048eafe" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_a3422826753d4e6b079dea98342" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "FK_e3aebe2bd1c53467a07109be596" FOREIGN KEY ("parentId") REFERENCES "comment" ("id") ON DELETE SET NULL ON UPDATE NO ACTION, + CONSTRAINT "FK_7a88834dadfa6fe261268bfceef" FOREIGN KEY ("employeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_35cddb3e66a46587966b68a9217" FOREIGN KEY ("resolvedByEmployeeId") REFERENCES "employee" ("id") ON DELETE CASCADE ON UPDATE NO ACTION + ) + `); + + // Step 8: Copy data from the temporary table (which now has the new columns) into the new clean table. + console.log('Step 8: Copying data into the new "temporary_comment" table without old columns...'); + await queryRunner.query(` + INSERT INTO "temporary_comment" ( + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "parentId", + "employeeId", + "resolvedByEmployeeId" + ) + SELECT + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "parentId", + "employeeId", + "resolvedByEmployeeId" + FROM "comment" + `); + + // Step 9: Drop the old "comment" table and rename the new clean table. + console.log('Step 9: Dropping old "comment" table and renaming "temporary_comment" to "comment"...'); + await queryRunner.query(`DROP TABLE "comment"`); + await queryRunner.query(`ALTER TABLE "temporary_comment" RENAME TO "comment"`); + + // Step 10: Create final indexes on the new "comment" table. + console.log('Step 10: Creating indexes on the new "comment" table...'); + await queryRunner.query(`CREATE INDEX "IDX_3620aeff4ac5c977176226017e" ON "comment" ("isActive")`); + await queryRunner.query(`CREATE INDEX "IDX_da3cd25ed3a6ce76770f00c3da" ON "comment" ("isArchived")`); + await queryRunner.query(`CREATE INDEX "IDX_8f58834bed39f0f9e85f048eaf" ON "comment" ("tenantId")`); + await queryRunner.query(`CREATE INDEX "IDX_a3422826753d4e6b079dea9834" ON "comment" ("organizationId")`); + await queryRunner.query(`CREATE INDEX "IDX_097e339f6cb990306d19880a4c" ON "comment" ("entity")`); + await queryRunner.query(`CREATE INDEX "IDX_2950cfa146fc50334efa61a70b" ON "comment" ("entityId")`); + await queryRunner.query(`CREATE INDEX "IDX_eecd6e41f9acb6bf59e474d518" ON "comment" ("actorType")`); + await queryRunner.query(`CREATE INDEX "IDX_e3aebe2bd1c53467a07109be59" ON "comment" ("parentId")`); + await queryRunner.query(`CREATE INDEX "IDX_7a88834dadfa6fe261268bfcee" ON "comment" ("employeeId")`); + await queryRunner.query(`CREATE INDEX "IDX_35cddb3e66a46587966b68a921" ON "comment" ("resolvedByEmployeeId")`); + } + + /** + * SqliteDB and BetterSQlite3DB Down Migration + * + * @param queryRunner + */ + public async sqliteDownQueryRunner(queryRunner: QueryRunner): Promise { + // Step 1: Drop existing indexes from the final "comment" table. + console.log('Step 1: Dropping existing indexes from "comment"...'); + await queryRunner.query(`DROP INDEX "IDX_35cddb3e66a46587966b68a921"`); + await queryRunner.query(`DROP INDEX "IDX_7a88834dadfa6fe261268bfcee"`); + await queryRunner.query(`DROP INDEX "IDX_e3aebe2bd1c53467a07109be59"`); + await queryRunner.query(`DROP INDEX "IDX_eecd6e41f9acb6bf59e474d518"`); + await queryRunner.query(`DROP INDEX "IDX_2950cfa146fc50334efa61a70b"`); + await queryRunner.query(`DROP INDEX "IDX_097e339f6cb990306d19880a4c"`); + await queryRunner.query(`DROP INDEX "IDX_a3422826753d4e6b079dea9834"`); + await queryRunner.query(`DROP INDEX "IDX_8f58834bed39f0f9e85f048eaf"`); + await queryRunner.query(`DROP INDEX "IDX_da3cd25ed3a6ce76770f00c3da"`); + await queryRunner.query(`DROP INDEX "IDX_3620aeff4ac5c977176226017e"`); + + // Step 2: Rename the current "comment" table to "temporary_comment". + console.log('Step 2: Renaming "comment" to "temporary_comment"...'); + await queryRunner.query(`ALTER TABLE "comment" RENAME TO "temporary_comment"`); + + // Step 3: Create a new "comment" table with the old schema. + // This new table includes "creatorId" and "resolvedById" with their foreign key constraints. + console.log('Step 3: Creating new "comment" table with old schema (with "creatorId" and "resolvedById")...'); + await queryRunner.query(` + CREATE TABLE "comment" ( + "deletedAt" datetime, + "id" varchar PRIMARY KEY NOT NULL, + "createdAt" datetime NOT NULL DEFAULT (datetime('now')), + "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), + "isActive" boolean DEFAULT (1), + "isArchived" boolean DEFAULT (0), + "archivedAt" datetime, + "tenantId" varchar, + "organizationId" varchar, + "entity" varchar NOT NULL, + "entityId" varchar NOT NULL, + "comment" text NOT NULL, + "actorType" integer, + "resolved" boolean, + "resolvedAt" datetime, + "editedAt" datetime, + "parentId" varchar, + "creatorId" varchar, + "resolvedById" varchar, + "employeeId" varchar, + "resolvedByEmployeeId" varchar, + CONSTRAINT "FK_8f58834bed39f0f9e85f048eafe" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_a3422826753d4e6b079dea98342" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "FK_e3aebe2bd1c53467a07109be596" FOREIGN KEY ("parentId") REFERENCES "comment" ("id") ON DELETE SET NULL ON UPDATE NO ACTION + ) + `); + + // Step 4: Copy data from the old "temporary_comment" table into the "comment" table. + console.log('Step 4: Copying data from the existing "temporary_comment" table into "comment"...'); + await queryRunner.query(` + INSERT INTO "comment" ( + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "parentId", + "employeeId", + "resolvedByEmployeeId" + ) + SELECT + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "parentId", + "employeeId", + "resolvedByEmployeeId" + FROM "temporary_comment" + `); + + // Step 5: Update "creatorId" from "employeeId" using employee table mapping + console.log('Step 5: Updating "creatorId" from "employeeId" via employee table mapping...'); + await queryRunner.query(` + UPDATE "comment" + SET "creatorId" = ( + SELECT e."userId" + FROM "employee" e + WHERE e."id" = "comment"."employeeId" + LIMIT 1 + ) + WHERE "employeeId" IS NOT NULL + `); + + // Step 6: Update "resolvedById" from "resolvedByEmployeeId" using employee table mapping + console.log('Step 6: Updating "resolvedById" from "resolvedByEmployeeId" via employee table mapping...'); + await queryRunner.query(` + UPDATE "comment" + SET "resolvedById" = ( + SELECT e."userId" + FROM "employee" e + WHERE e."id" = "comment"."resolvedByEmployeeId" + LIMIT 1 + ) + WHERE "resolvedByEmployeeId" IS NOT NULL + `); + + // Step 7: Drop the old "comment" table and rename the temporary table to it. + console.log('Step 7: Dropping the old "temporary_comment" table and renaming the "comment" table to it...'); + await queryRunner.query(`DROP TABLE "temporary_comment"`); + await queryRunner.query(`ALTER TABLE "comment" RENAME TO "temporary_comment"`); + + // Step 8: Recreating "comment" table with the new columns. + console.log('Step 8: Recreating "comment" table with the new columns...'); + await queryRunner.query(` + CREATE TABLE "comment" ( + "deletedAt" datetime, + "id" varchar PRIMARY KEY NOT NULL, + "createdAt" datetime NOT NULL DEFAULT (datetime('now')), + "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), + "isActive" boolean DEFAULT (1), + "isArchived" boolean DEFAULT (0), + "archivedAt" datetime, + "tenantId" varchar, + "organizationId" varchar, + "entity" varchar NOT NULL, + "entityId" varchar NOT NULL, + "comment" text NOT NULL, + "actorType" integer, + "resolved" boolean, + "resolvedAt" datetime, + "editedAt" datetime, + "creatorId" varchar, + "resolvedById" varchar, + "parentId" varchar, + CONSTRAINT "FK_8f58834bed39f0f9e85f048eafe" FOREIGN KEY ("tenantId") REFERENCES "tenant" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_a3422826753d4e6b079dea98342" FOREIGN KEY ("organizationId") REFERENCES "organization" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "FK_b6bf60ecb9f6c398e349adff52f" FOREIGN KEY ("creatorId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_c9409c81aa283c1aae70fd5f4c3" FOREIGN KEY ("resolvedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, + CONSTRAINT "FK_e3aebe2bd1c53467a07109be596" FOREIGN KEY ("parentId") REFERENCES "comment" ("id") ON DELETE SET NULL ON UPDATE NO ACTION + ) + `); + + // Step 9: Copy data from the old "temporary_comment" table into the "comment" table. + console.log('Step 9: Copying data from the existing "temporary_comment" table into "comment"...'); + await queryRunner.query(` + INSERT INTO "comment" ( + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "creatorId", + "resolvedById", + "parentId" + ) + SELECT + "deletedAt", + "id", + "createdAt", + "updatedAt", + "isActive", + "isArchived", + "archivedAt", + "tenantId", + "organizationId", + "entity", + "entityId", + "comment", + "actorType", + "resolved", + "resolvedAt", + "editedAt", + "creatorId", + "resolvedById", + "parentId" + FROM "temporary_comment" + `); + + // Step 10: Dropping the old "temporary_comment" table. + console.log('Step 10: Dropping the old "temporary_comment" table...'); + await queryRunner.query(`DROP TABLE "temporary_comment"`); + + // Step 11: Create final indexes on the new "comment" table. + console.log('Step 11: Creating indexes on the new "comment" table...'); + await queryRunner.query(`CREATE INDEX "IDX_e3aebe2bd1c53467a07109be59" ON "comment" ("parentId") `); + await queryRunner.query(`CREATE INDEX "IDX_c9409c81aa283c1aae70fd5f4c" ON "comment" ("resolvedById") `); + await queryRunner.query(`CREATE INDEX "IDX_b6bf60ecb9f6c398e349adff52" ON "comment" ("creatorId") `); + await queryRunner.query(`CREATE INDEX "IDX_eecd6e41f9acb6bf59e474d518" ON "comment" ("actorType") `); + await queryRunner.query(`CREATE INDEX "IDX_2950cfa146fc50334efa61a70b" ON "comment" ("entityId") `); + await queryRunner.query(`CREATE INDEX "IDX_097e339f6cb990306d19880a4c" ON "comment" ("entity") `); + await queryRunner.query(`CREATE INDEX "IDX_a3422826753d4e6b079dea9834" ON "comment" ("organizationId") `); + await queryRunner.query(`CREATE INDEX "IDX_8f58834bed39f0f9e85f048eaf" ON "comment" ("tenantId") `); + await queryRunner.query(`CREATE INDEX "IDX_da3cd25ed3a6ce76770f00c3da" ON "comment" ("isArchived") `); + await queryRunner.query(`CREATE INDEX "IDX_3620aeff4ac5c977176226017e" ON "comment" ("isActive") `); + } + + /** + * MySQL Up Migration + * + * @param queryRunner + */ + public async mysqlUpQueryRunner(queryRunner: QueryRunner): Promise { + // Step 1: Drop existing foreign keys on "creatorId" and "resolvedById" + console.log('Step 1: Dropping existing foreign keys...'); + await queryRunner.query(`ALTER TABLE \`comment\` DROP FOREIGN KEY \`FK_b6bf60ecb9f6c398e349adff52f\``); + await queryRunner.query(`ALTER TABLE \`comment\` DROP FOREIGN KEY \`FK_c9409c81aa283c1aae70fd5f4c3\``); + + // Step 2: Drop existing indexes on "creatorId" and "resolvedById" + console.log('Step 2: Dropping existing indexes on "creatorId" and "resolvedById"...'); + await queryRunner.query(`DROP INDEX \`IDX_c9409c81aa283c1aae70fd5f4c\` ON \`comment\``); + await queryRunner.query(`DROP INDEX \`IDX_b6bf60ecb9f6c398e349adff52\` ON \`comment\``); + + // Step 3: Add new columns "employeeId" and "resolvedByEmployeeId". + console.log('Step 3: Adding new columns "employeeId" and "resolvedByEmployeeId" to "comment"...'); + await queryRunner.query(`ALTER TABLE \`comment\` ADD \`employeeId\` varchar(255) NULL`); + await queryRunner.query(`ALTER TABLE \`comment\` ADD \`resolvedByEmployeeId\` varchar(255) NULL`); + + // Step 4: Copy data from "creatorId" to "employeeId" using the employee mapping + console.log('Step 4: Copying data from "creatorId" to "employeeId" via employee table mapping...'); + await queryRunner.query(` + UPDATE \`comment\` c + JOIN \`employee\` e ON c.\`creatorId\` = e.\`userId\` + SET c.\`employeeId\` = e.\`id\` + WHERE c.\`creatorId\` IS NOT NULL + `); + + // Step 5: Copy data from "resolvedById" to "resolvedByEmployeeId" using the employee mapping + console.log('Step 5: Copying data from "resolvedById" to "resolvedByEmployeeId" via employee table mapping...'); + await queryRunner.query(` + UPDATE \`comment\` c + JOIN \`employee\` e ON c.\`resolvedById\` = e.\`userId\` + SET c.\`resolvedByEmployeeId\` = e.\`id\` + WHERE c.\`resolvedById\` IS NOT NULL + `); + + // Step 6: Drop the old columns "creatorId" and "resolvedById". + console.log('Step 6: Dropping old columns "creatorId" and "resolvedById"...'); + await queryRunner.query(`ALTER TABLE \`comment\` DROP COLUMN \`creatorId\``); + await queryRunner.query(`ALTER TABLE \`comment\` DROP COLUMN \`resolvedById\``); + + // Step 6: Create indexes on the new columns. + console.log('Step 7: Creating indexes on "employeeId" and "resolvedByEmployeeId"...'); + await queryRunner.query(`CREATE INDEX \`IDX_7a88834dadfa6fe261268bfcee\` ON \`comment\` (\`employeeId\`)`); + await queryRunner.query( + `CREATE INDEX \`IDX_35cddb3e66a46587966b68a921\` ON \`comment\` (\`resolvedByEmployeeId\`)` + ); + + // Step 8: Add foreign key constraint for "employeeId" + console.log('Step 8: Adding foreign key constraint for "employeeId"...'); + await queryRunner.query(` + ALTER TABLE \`comment\` ADD CONSTRAINT \`FK_7a88834dadfa6fe261268bfceef\` + FOREIGN KEY (\`employeeId\`) REFERENCES \`employee\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION + `); + + // Step 9: Add foreign key constraint for "resolvedByEmployeeId" + console.log('Step 9: Adding foreign key constraint for "resolvedByEmployeeId"...'); + await queryRunner.query(` + ALTER TABLE \`comment\` ADD CONSTRAINT \`FK_35cddb3e66a46587966b68a9217\` + FOREIGN KEY (\`resolvedByEmployeeId\`) REFERENCES \`employee\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION + `); + } + + /** + * MySQL Down Migration + * + * @param queryRunner + */ + public async mysqlDownQueryRunner(queryRunner: QueryRunner): Promise { + // Step 1: Drop foreign key constraints for "resolvedByEmployeeId" and "employeeId" + console.log('Step 1: Dropping foreign key constraints for "resolvedByEmployeeId" and "employeeId"...'); + await queryRunner.query(`ALTER TABLE \`comment\` DROP FOREIGN KEY \`FK_35cddb3e66a46587966b68a9217\``); + await queryRunner.query(`ALTER TABLE \`comment\` DROP FOREIGN KEY \`FK_7a88834dadfa6fe261268bfceef\``); + + // Step 2: Drop indexes on "resolvedByEmployeeId" and "employeeId" + console.log('Step 2: Dropping indexes on "resolvedByEmployeeId" and "employeeId"...'); + await queryRunner.query(`DROP INDEX \`IDX_35cddb3e66a46587966b68a921\` ON \`comment\``); + await queryRunner.query(`DROP INDEX \`IDX_7a88834dadfa6fe261268bfcee\` ON \`comment\``); + + // Step 3: Re-add the old columns "resolvedById" and "creatorId" + console.log('Step 3: Adding columns "resolvedById" and "creatorId"...'); + await queryRunner.query(`ALTER TABLE \`comment\` ADD \`resolvedById\` varchar(255) NULL`); + await queryRunner.query(`ALTER TABLE \`comment\` ADD \`creatorId\` varchar(255) NULL`); + + // Step 4: Copy data from "employeeId" to "creatorId" via employee table mapping + console.log('Step 4: Copying data from "employeeId" to "creatorId" via employee table mapping...'); + await queryRunner.query(` + UPDATE \`comment\` c + JOIN \`employee\` e ON c.\`employeeId\` = e.\`id\` + SET c.\`creatorId\` = e.\`userId\` + WHERE c.\`employeeId\` IS NOT NULL + `); + + // Step 5: Copy data from "resolvedByEmployeeId" to "resolvedById" via employee table mapping + console.log('Step 5: Copying data from "resolvedByEmployeeId" to "resolvedById" via employee table mapping...'); + await queryRunner.query(` + UPDATE \`comment\` c + JOIN \`employee\` e ON c.\`resolvedByEmployeeId\` = e.\`id\` + SET c.\`resolvedById\` = e.\`userId\` + WHERE c.\`resolvedByEmployeeId\` IS NOT NULL + `); + + // Step 6: Drop the new columns "resolvedByEmployeeId" and "employeeId" + console.log('Step 6: Dropping columns "resolvedByEmployeeId" and "employeeId"...'); + await queryRunner.query(`ALTER TABLE \`comment\` DROP COLUMN \`resolvedByEmployeeId\``); + await queryRunner.query(`ALTER TABLE \`comment\` DROP COLUMN \`employeeId\``); + + // Step 7: Create indexes on the restored columns "resolvedById" and "creatorId" + console.log('Step 7: Creating indexes on "resolvedById" and "creatorId"...'); + await queryRunner.query(`CREATE INDEX \`IDX_b6bf60ecb9f6c398e349adff52\` ON \`comment\` (\`creatorId\`)`); + await queryRunner.query(`CREATE INDEX \`IDX_c9409c81aa283c1aae70fd5f4c\` ON \`comment\` (\`resolvedById\`)`); + + // Step 8: Add foreign key constraint for "resolvedById" referencing "user" + console.log('Step 8: Adding foreign key constraint for "resolvedById" referencing "user"...'); + await queryRunner.query( + `ALTER TABLE \`comment\` ADD CONSTRAINT \`FK_c9409c81aa283c1aae70fd5f4c3\` FOREIGN KEY (\`resolvedById\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + + // Step 9: Add foreign key constraint for "creatorId" referencing "user" + console.log('Step 9: Adding foreign key constraint for "creatorId" referencing "user"...'); + await queryRunner.query( + `ALTER TABLE \`comment\` ADD CONSTRAINT \`FK_b6bf60ecb9f6c398e349adff52f\` FOREIGN KEY (\`creatorId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE NO ACTION` + ); + } +} diff --git a/packages/core/src/lib/employee-notification/employee-notification.service.ts b/packages/core/src/lib/employee-notification/employee-notification.service.ts index 234d6abf1b2..6331c6190db 100644 --- a/packages/core/src/lib/employee-notification/employee-notification.service.ts +++ b/packages/core/src/lib/employee-notification/employee-notification.service.ts @@ -100,7 +100,7 @@ export class EmployeeNotificationService extends TenantAwareCrudService Array }) @IsOptional() @IsArray() - mentionUserIds?: ID[]; + mentionEmployeeIds?: ID[]; } diff --git a/packages/core/src/lib/mention/mention.service.ts b/packages/core/src/lib/mention/mention.service.ts index 5eb0151591e..ceb5574ccbf 100644 --- a/packages/core/src/lib/mention/mention.service.ts +++ b/packages/core/src/lib/mention/mention.service.ts @@ -108,18 +108,18 @@ export class MentionService extends TenantAwareCrudService { * * This method handles adding new mentions and removing outdated mentions * for an entity (e.g., comments, tasks, or projects). It ensures that only - * the specified user mentions (`newMentionUserIds`) are associated with the entity. + * the specified user mentions (`newMentionEmployeeIds`) are associated with the entity. * * @param entity - The type of entity being updated (e.g., Comment, Task, etc.). * @param entityId - The ID of the entity being updated. - * @param mentionUserIds - Array of user IDs to be mentioned in this entity. + * @param mentionEmployeeIds - Array of user IDs to be mentioned in this entity. * @param parentEntityId - (Optional) The ID of the parent entity, if applicable. * @param parentEntityType - (Optional) The type of the parent entity, if applicable. */ async updateEntityMentions( entity: BaseEntityEnum, entityId: ID, - mentionUserIds: ID[], + mentionEmployeeIds: ID[], parentEntityId?: ID, parentEntityType?: BaseEntityEnum ): Promise { @@ -133,13 +133,13 @@ export class MentionService extends TenantAwareCrudService { }); // Extract the IDs of currently mentioned users - const existingMentionUserIds = new Set(existingMentions.map((mention) => mention.mentionedUserId)); + const existingMentionEmployeeIds = new Set(existingMentions.map((mention) => mention.mentionedUserId)); // Determine mentions to add (not present in existing mentions) - const mentionsToAdd = mentionUserIds.filter((id) => !existingMentionUserIds.has(id)); + const mentionsToAdd = mentionEmployeeIds.filter((id) => !existingMentionEmployeeIds.has(id)); // Determine mentions to remove (present in existing mentions but not in mentionsIds) - const mentionsToRemove = [...existingMentionUserIds].filter((id) => !mentionUserIds.includes(id)); + const mentionsToRemove = [...existingMentionEmployeeIds].filter((id) => !mentionEmployeeIds.includes(id)); // Add new mentions if (mentionsToAdd.length > 0) { diff --git a/packages/core/src/lib/subscription/events/handlers/subscription.create.handler.ts b/packages/core/src/lib/subscription/events/handlers/subscription.create.handler.ts index f4ee82c2645..3bcc00cbc0a 100644 --- a/packages/core/src/lib/subscription/events/handlers/subscription.create.handler.ts +++ b/packages/core/src/lib/subscription/events/handlers/subscription.create.handler.ts @@ -1,3 +1,4 @@ +import { BadRequestException } from '@nestjs/common'; import { CommandBus, EventsHandler, IEventHandler } from '@nestjs/cqrs'; import { IEntitySubscription } from '@gauzy/contracts'; import { CreateSubscriptionEvent } from '../subscription.create.event'; @@ -8,14 +9,37 @@ export class CreateSubscriptionHandler implements IEventHandler} A promise that resolves to the created subscription. + * Extracts subscription details from the event input and uses the command bus to execute a SubscriptionCreateCommand, + * which creates a new subscription for the specified entity. * + * @param {CreateSubscriptionEvent} event - The event containing the input data for subscription creation. + * @returns {Promise} A promise that resolves to the created subscription. + * @throws An error if the subscription creation process fails. */ async handle(event: CreateSubscriptionEvent): Promise { - const { input } = event; - return await this.commandBus.execute(new SubscriptionCreateCommand(input)); + try { + // Retrieve the input data from the event. + const { entity, entityId, userId, type, organizationId, tenantId } = event.input; + + // Execute the subscription creation command. + const subscription = await this.commandBus.execute( + new SubscriptionCreateCommand({ + entity, + entityId, + userId, + type, + organizationId, + tenantId + }) + ); + + return subscription; + } catch (error) { + console.log(`Error while creating subscription: ${error.message}`, error); + // Re-throw the error with additional context + throw new BadRequestException('Failed to create subscription', error); + } } } diff --git a/packages/core/src/lib/subscription/events/subscription.create.event.ts b/packages/core/src/lib/subscription/events/subscription.create.event.ts index 5b898a178ac..f3f565532a1 100644 --- a/packages/core/src/lib/subscription/events/subscription.create.event.ts +++ b/packages/core/src/lib/subscription/events/subscription.create.event.ts @@ -2,5 +2,5 @@ import { IEvent } from '@nestjs/cqrs'; import { IEntitySubscriptionCreateInput } from '@gauzy/contracts'; export class CreateSubscriptionEvent implements IEvent { - constructor(public readonly input: IEntitySubscriptionCreateInput) {} + constructor(readonly input: IEntitySubscriptionCreateInput) {} } diff --git a/packages/core/src/lib/tasks/commands/handlers/task-create.handler.ts b/packages/core/src/lib/tasks/commands/handlers/task-create.handler.ts index b3502d4ca12..8c299fa985e 100644 --- a/packages/core/src/lib/tasks/commands/handlers/task-create.handler.ts +++ b/packages/core/src/lib/tasks/commands/handlers/task-create.handler.ts @@ -51,7 +51,7 @@ export class TaskCreateHandler implements ICommandHandler { try { // Destructure input and triggered event flag from the command const { input, triggeredEvent } = command; - const { organizationId, mentionUserIds = [], members = [], ...data } = input; + const { organizationId, mentionEmployeeIds = [], members = [], ...data } = input; // Retrieve current tenant ID from request context or use input tenant ID const tenantId = RequestContext.currentTenantId() ?? data.tenantId; @@ -96,9 +96,9 @@ export class TaskCreateHandler implements ICommandHandler { } // Apply mentions if needed - if (mentionUserIds.length > 0) { + if (mentionEmployeeIds.length > 0) { await Promise.all( - mentionUserIds.map((mentionedUserId: ID) => + mentionEmployeeIds.map((mentionedUserId: ID) => this.mentionService.publishMention({ entity: BaseEntityEnum.Task, entityId: task.id, diff --git a/packages/core/src/lib/tasks/dto/create-task.dto.ts b/packages/core/src/lib/tasks/dto/create-task.dto.ts index 2f60e916096..a71eca3e9f1 100644 --- a/packages/core/src/lib/tasks/dto/create-task.dto.ts +++ b/packages/core/src/lib/tasks/dto/create-task.dto.ts @@ -1,7 +1,7 @@ import { ITaskCreateInput } from '@gauzy/contracts'; import { IntersectionType, OmitType } from '@nestjs/swagger'; import { TenantOrganizationBaseDTO } from './../../core/dto'; -import { MentionUserIdsDTO } from '../../mention/dto'; +import { MentionEmployeeIdsDTO } from '../../mention/dto'; import { Task } from './../task.entity'; /** @@ -10,6 +10,6 @@ import { Task } from './../task.entity'; export class CreateTaskDTO extends IntersectionType( TenantOrganizationBaseDTO, - IntersectionType(OmitType(Task, ['organizationId', 'organization']), MentionUserIdsDTO) + IntersectionType(OmitType(Task, ['organizationId', 'organization']), MentionEmployeeIdsDTO) ) implements ITaskCreateInput {} diff --git a/packages/core/src/lib/tasks/screening-tasks/dto/create-screening-task.dto.ts b/packages/core/src/lib/tasks/screening-tasks/dto/create-screening-task.dto.ts index ee374310636..d7f726e9bb1 100644 --- a/packages/core/src/lib/tasks/screening-tasks/dto/create-screening-task.dto.ts +++ b/packages/core/src/lib/tasks/screening-tasks/dto/create-screening-task.dto.ts @@ -1,15 +1,15 @@ import { IntersectionType, OmitType } from '@nestjs/swagger'; import { IScreeningTaskCreateInput } from '@gauzy/contracts'; import { TenantOrganizationBaseDTO } from '../../../core/dto'; -import { MentionUserIdsDTO } from '../../../mention/dto'; +import { MentionEmployeeIdsDTO } from '../../../mention/dto'; import { ScreeningTask } from '../screening-task.entity'; /** - * Create Sreening Task data validation request DTO + * Create Screening Task data validation request DTO */ export class CreateScreeningTaskDTO extends IntersectionType( TenantOrganizationBaseDTO, - IntersectionType(OmitType(ScreeningTask, ['status']), MentionUserIdsDTO) + IntersectionType(OmitType(ScreeningTask, ['status']), MentionEmployeeIdsDTO) ) implements IScreeningTaskCreateInput {} diff --git a/packages/core/src/lib/tasks/screening-tasks/dto/update-screening-task.dto.ts b/packages/core/src/lib/tasks/screening-tasks/dto/update-screening-task.dto.ts index 1ab9cd91392..56fdefba2b2 100644 --- a/packages/core/src/lib/tasks/screening-tasks/dto/update-screening-task.dto.ts +++ b/packages/core/src/lib/tasks/screening-tasks/dto/update-screening-task.dto.ts @@ -4,7 +4,7 @@ import { TenantOrganizationBaseDTO } from '../../../core/dto'; import { ScreeningTask } from '../screening-task.entity'; /** - * Update Sreening Task data validation request DTO + * Update Screening Task data validation request DTO */ export class UpdateScreeningTaskDTO extends IntersectionType(TenantOrganizationBaseDTO, OmitType(ScreeningTask, ['task', 'taskId'])) diff --git a/packages/core/src/lib/tasks/screening-tasks/screening-tasks.service.ts b/packages/core/src/lib/tasks/screening-tasks/screening-tasks.service.ts index 935a7ca1298..90205602a19 100644 --- a/packages/core/src/lib/tasks/screening-tasks/screening-tasks.service.ts +++ b/packages/core/src/lib/tasks/screening-tasks/screening-tasks.service.ts @@ -49,7 +49,7 @@ export class ScreeningTasksService extends TenantAwareCrudService try { const userId = RequestContext.currentUserId(); const tenantId = RequestContext.currentTenantId() || input.tenantId; - const { organizationId, mentionUserIds = [] } = input; + const { organizationId, mentionEmployeeIds = [] } = input; // Check if projectId is provided, if not use the provided project object from the input. // If neither is provided, set project to null. @@ -94,7 +94,7 @@ export class ScreeningTasksService extends TenantAwareCrudService }); // Apply mentions if needed - const mentionPromises = mentionUserIds.map((mentionedUserId) => + const mentionPromises = mentionEmployeeIds.map((mentionedUserId) => this.mentionService.publishMention({ entity: BaseEntityEnum.Task, entityId: task.id, diff --git a/packages/core/src/lib/tasks/task.service.ts b/packages/core/src/lib/tasks/task.service.ts index 95e747bb04b..4c8d8f0aa62 100644 --- a/packages/core/src/lib/tasks/task.service.ts +++ b/packages/core/src/lib/tasks/task.service.ts @@ -79,7 +79,7 @@ export class TaskService extends TenantAwareCrudService { const userId = RequestContext.currentUserId(); const user = RequestContext.currentUser(); - const { mentionUserIds, ...data } = input; + const { mentionEmployeeIds, ...data } = input; // Find task relations const relations: FindOptionsRelations = { @@ -148,7 +148,7 @@ export class TaskService extends TenantAwareCrudService { // Synchronize mentions if (data.description) { try { - await this.mentionService.updateEntityMentions(BaseEntityEnum.Task, id, mentionUserIds); + await this.mentionService.updateEntityMentions(BaseEntityEnum.Task, id, mentionEmployeeIds); } catch (error) { console.error('Error synchronizing mentions:', error); }