From 2fd3f1fa7561843887db09f3dfb7351a707817f7 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 08:43:54 +0200 Subject: [PATCH 01/15] feat: create a helper function to map issue relation description --- packages/contracts/src/base-entity.model.ts | 1 + .../src/activity-log/activity-log.service.ts | 2 +- .../linked-issue/task-linked-issue.helper.ts | 21 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts diff --git a/packages/contracts/src/base-entity.model.ts b/packages/contracts/src/base-entity.model.ts index 81951b07154..86f41428ca2 100644 --- a/packages/contracts/src/base-entity.model.ts +++ b/packages/contracts/src/base-entity.model.ts @@ -87,5 +87,6 @@ export enum BaseEntityEnum { OrganizationVendor = 'OrganizationVendor', Task = 'Task', TaskView = 'TaskView', + TaskLinkedIssue = 'TaskLinkedIssue', User = 'User' } diff --git a/packages/core/src/activity-log/activity-log.service.ts b/packages/core/src/activity-log/activity-log.service.ts index fa18e7334ac..1019cc68d37 100644 --- a/packages/core/src/activity-log/activity-log.service.ts +++ b/packages/core/src/activity-log/activity-log.service.ts @@ -122,7 +122,7 @@ export class ActivityLogService extends TenantAwareCrudService { /** * @description Create or Update Activity Log * @template T - * @param {BaseEntityEnum} entityType - Entity type for whom creating activity log (E.g : Task, OrganizationProject, etc.) + * @param {BaseEntityEnum} entity - Entity type for whom creating activity log (E.g : Task, OrganizationProject, etc.) * @param {string} entityName - Name or Title of the entity * @param {ActorTypeEnum} actor - The actor type performing the action (User or System) * @param {ID} organizationId diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts new file mode 100644 index 00000000000..f8843b9871b --- /dev/null +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts @@ -0,0 +1,21 @@ +import { TaskRelatedIssuesRelationEnum } from '@gauzy/contracts'; + +/** + * Maps a task's related issue relation enum to a corresponding string description. + * + * @param {TaskRelatedIssuesRelationEnum} relation - The relation type from the enum `TaskRelatedIssuesRelationEnum`. + * @returns {string} The corresponding string description for the given relation type. + */ +export function taskRelatedIssueRelationMap(relation: TaskRelatedIssuesRelationEnum): string { + const issueRelationMap: { [key in TaskRelatedIssuesRelationEnum]: string } = { + [TaskRelatedIssuesRelationEnum.BLOCKS]: 'Blocks', + [TaskRelatedIssuesRelationEnum.CLONES]: 'Clones', + [TaskRelatedIssuesRelationEnum.DUPLICATES]: 'Duplicates', + [TaskRelatedIssuesRelationEnum.IS_BLOCKED_BY]: 'Is Blocked By', + [TaskRelatedIssuesRelationEnum.IS_CLONED_BY]: 'Is cloned By', + [TaskRelatedIssuesRelationEnum.IS_DUPLICATED_BY]: 'Is Duplicated By', + [TaskRelatedIssuesRelationEnum.RELATES_TO]: 'Relates To' + }; + + return issueRelationMap[relation]; +} From 38de69c73ed7607622e96f24152419a451b3a903 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 08:58:08 +0200 Subject: [PATCH 02/15] fix: improve task linked issue creation supporting activity log --- .../contracts/src/task-linked-issue.model.ts | 16 +++--- .../linked-issue/dto/task-linked-issue.dto.ts | 22 +++----- .../task-linked-issue.controller.ts | 4 +- .../linked-issue/task-linked-issue.entity.ts | 15 ++---- .../linked-issue/task-linked-issue.service.ts | 53 +++++++++++++++++-- 5 files changed, 69 insertions(+), 41 deletions(-) diff --git a/packages/contracts/src/task-linked-issue.model.ts b/packages/contracts/src/task-linked-issue.model.ts index a7b0f71d6dc..0cac874aaa8 100644 --- a/packages/contracts/src/task-linked-issue.model.ts +++ b/packages/contracts/src/task-linked-issue.model.ts @@ -1,4 +1,4 @@ -import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model'; +import { IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model'; import { ITask } from './task.model'; export enum TaskRelatedIssuesRelationEnum { @@ -8,23 +8,21 @@ export enum TaskRelatedIssuesRelationEnum { CLONES = 4, IS_DUPLICATED_BY = 5, DUPLICATES = 6, - RELATES_TO = 7, + RELATES_TO = 7 } -export interface ITaskLinkedIssue - extends IBasePerTenantAndOrganizationEntityModel { +export interface ITaskLinkedIssue extends IBasePerTenantAndOrganizationEntityModel { action: TaskRelatedIssuesRelationEnum; taskFrom?: ITask; - taskFromId: ITask['id']; + taskFromId: ID; taskTo?: ITask; - taskToId: ITask['id']; + taskToId: ID; } export interface ITaskLinkedIssueCreateInput extends ITaskLinkedIssue {} -export interface ITaskLinkedIssueUpdateInput - extends Partial { - id?: string; +export interface ITaskLinkedIssueUpdateInput extends Partial { + id?: ID; } export interface ILinkedIssueFindInput diff --git a/packages/core/src/tasks/linked-issue/dto/task-linked-issue.dto.ts b/packages/core/src/tasks/linked-issue/dto/task-linked-issue.dto.ts index 48eb54fd381..e43b0e0262f 100644 --- a/packages/core/src/tasks/linked-issue/dto/task-linked-issue.dto.ts +++ b/packages/core/src/tasks/linked-issue/dto/task-linked-issue.dto.ts @@ -1,18 +1,8 @@ -import { TaskRelatedIssuesRelationEnum } from '@gauzy/contracts'; -import { ApiProperty } from '@nestjs/swagger'; +import { ITaskLinkedIssue } from '@gauzy/contracts'; +import { IntersectionType } from '@nestjs/swagger'; import { TenantOrganizationBaseDTO } from '../../../core/dto'; -import { IsEnum, IsUUID } from 'class-validator'; +import { TaskLinkedIssue } from '../task-linked-issue.entity'; -export class TaskLinkedIssueDTO extends TenantOrganizationBaseDTO { - @ApiProperty({ type: () => String, enum: TaskRelatedIssuesRelationEnum }) - @IsEnum(TaskRelatedIssuesRelationEnum) - action: TaskRelatedIssuesRelationEnum; - - @ApiProperty({ type: () => String }) - @IsUUID() - taskFromId: string; - - @ApiProperty({ type: () => String }) - @IsUUID() - taskToId: string; -} +export class TaskLinkedIssueDTO + extends IntersectionType(TenantOrganizationBaseDTO, TaskLinkedIssue) + implements ITaskLinkedIssue {} diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index ed6b90881e7..7113aa0facd 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -1,6 +1,6 @@ import { Body, Controller, HttpCode, HttpStatus, Param, Post, Put, UseGuards } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; +import { ID, ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from '../../shared/guards'; import { UUIDValidationPipe, UseValidationPipe } from '../../shared/pipes'; import { Permissions } from '../../shared/decorators'; @@ -44,7 +44,7 @@ export class TaskLinkedIssueController extends CrudController { @Put(':id') @UseValidationPipe({ whitelist: true }) async update( - @Param('id', UUIDValidationPipe) id: ITaskLinkedIssue['id'], + @Param('id', UUIDValidationPipe) id: ID, @Body() entity: UpdateTaskLinkedIssueDTO ): Promise { return await this.taskLinkedIssueService.create({ diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts index 3ccdd23db02..c971d2b57cb 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts @@ -1,14 +1,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { - JoinColumn, - RelationId, -} from 'typeorm'; +import { JoinColumn, RelationId } from 'typeorm'; import { IsEnum, IsUUID } from 'class-validator'; -import { - ITask, - ITaskLinkedIssue, - TaskRelatedIssuesRelationEnum, -} from '@gauzy/contracts'; +import { ID, ITask, ITaskLinkedIssue, TaskRelatedIssuesRelationEnum } from '@gauzy/contracts'; import { Task } from './../task.entity'; import { TenantOrganizationBaseEntity } from './../../core/entities/internal'; import { ColumnIndex, MultiORMColumn, MultiORMEntity, MultiORMManyToOne } from './../../core/decorators/entity'; @@ -36,7 +29,7 @@ export class TaskLinkedIssue extends TenantOrganizationBaseEntity implements ITa @RelationId((it: TaskLinkedIssue) => it.taskFrom) @ColumnIndex() @MultiORMColumn({ relationId: true }) - taskFromId: ITask['id']; + taskFromId: ID; /** * Task Linked Issues @@ -51,5 +44,5 @@ export class TaskLinkedIssue extends TenantOrganizationBaseEntity implements ITa @RelationId((it: TaskLinkedIssue) => it.taskTo) @ColumnIndex() @MultiORMColumn({ relationId: true }) - taskToId: ITask['id']; + taskToId: ID; } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index b713da8f458..f1edf0dfbf8 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -1,9 +1,19 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { TaskLinkedIssue } from './task-linked-issue.entity'; +import { + ActionTypeEnum, + ActorTypeEnum, + BaseEntityEnum, + ITaskLinkedIssue, + ITaskLinkedIssueCreateInput +} from '@gauzy/contracts'; import { TenantAwareCrudService } from '../../core/crud'; +import { RequestContext } from '../../core/context'; +import { ActivityLogService } from '../../activity-log/activity-log.service'; +import { TaskLinkedIssue } from './task-linked-issue.entity'; import { MikroOrmTaskLinkedIssueRepository } from './repository/mikro-orm-linked-issue.repository'; import { TypeOrmTaskLinkedIssueRepository } from './repository/type-orm-linked-issue.repository'; +import { taskRelatedIssueRelationMap } from './task-linked-issue.helper'; @Injectable() export class TaskLinkedIssueService extends TenantAwareCrudService { @@ -11,8 +21,45 @@ export class TaskLinkedIssueService extends TenantAwareCrudService} The created task linked issue. + * @throws {HttpException} Throws a Bad Request exception if task creation fails. + * + */ + async create(entity: ITaskLinkedIssueCreateInput): Promise { + const tenantId = RequestContext.currentTenantId() || entity.tenantId; + const { organizationId } = entity; + + try { + const taskLinkedIssue = await super.create({ ...entity, tenantId }); + + // Generate the activity log + this.activityLogService.logActivity( + BaseEntityEnum.TaskLinkedIssue, + ActionTypeEnum.Created, + ActorTypeEnum.User, + taskLinkedIssue.id, + taskRelatedIssueRelationMap(taskLinkedIssue.action), + taskLinkedIssue, + organizationId, + tenantId + ); + + // Return the created task linked issue + return taskLinkedIssue; + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to create task linked issue : ${error.message}`, HttpStatus.BAD_REQUEST); + } + } } From 06b9d20489b6349768977e73b94f26522ca61408 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 09:20:08 +0200 Subject: [PATCH 03/15] fix: issue updating sprint --- .../linked-issue/task-linked-issue.service.ts | 52 ++++++++++++++++++- packages/core/src/tasks/task.service.ts | 2 +- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index f1edf0dfbf8..e557c11f9c5 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -1,11 +1,13 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ActionTypeEnum, ActorTypeEnum, BaseEntityEnum, + ID, ITaskLinkedIssue, - ITaskLinkedIssueCreateInput + ITaskLinkedIssueCreateInput, + ITaskLinkedIssueUpdateInput } from '@gauzy/contracts'; import { TenantAwareCrudService } from '../../core/crud'; import { RequestContext } from '../../core/context'; @@ -62,4 +64,50 @@ export class TaskLinkedIssueService extends TenantAwareCrudService} The updated task linked issue. + * @throws {HttpException} Throws a Bad Request exception if the update fails. + * @throws {NotFoundException} Throws a Not Found exception if the task linked issue does not exist. + * + */ + async update(id: ID, input: ITaskLinkedIssueUpdateInput): Promise { + const tenantId = RequestContext.currentTenantId() || input.tenantId; + + try { + // Retrieve existing task linked issue + const existingTaskLinkedIssue = await this.findOneByIdString(id); + + if (!existingTaskLinkedIssue) { + throw new NotFoundException('View not found'); + } + + const updatedTaskLinkedIssue = await super.create({ ...input, tenantId, id }); + + // Generate the activity log + const { organizationId } = updatedTaskLinkedIssue; + this.activityLogService.logActivity( + BaseEntityEnum.TaskLinkedIssue, + ActionTypeEnum.Updated, + ActorTypeEnum.User, + updatedTaskLinkedIssue.id, + taskRelatedIssueRelationMap(updatedTaskLinkedIssue.action), + updatedTaskLinkedIssue, + organizationId, + tenantId, + existingTaskLinkedIssue, + input + ); + + // return the updated task linked issue + return updatedTaskLinkedIssue; + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to update task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); + } + } } diff --git a/packages/core/src/tasks/task.service.ts b/packages/core/src/tasks/task.service.ts index 61cca691699..292208348c9 100644 --- a/packages/core/src/tasks/task.service.ts +++ b/packages/core/src/tasks/task.service.ts @@ -89,7 +89,7 @@ export class TaskService extends TenantAwareCrudService { // Register Task Sprint moving history if (organizationSprintId && organizationSprintId !== task.organizationSprintId) { await this.typeOrmOrganizationSprintTaskHistoryRepository.save({ - fromSprintId: task.organizationSprintId, + fromSprintId: task.organizationSprintId || organizationSprintId, // Use incoming sprint ID if the task's organizationSprintId was priviously null or undefined toSprintId: organizationSprintId, taskId: updatedTask.id, movedById: userId, From 7196224a3399105d7842d6473ac37b91e05f4dc9 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 09:27:48 +0200 Subject: [PATCH 04/15] feat: add task linked issue commands --- .../src/tasks/linked-issue/commands/handlers/index.ts | 0 .../commands/handlers/task-linked-issue-create.handler.ts | 0 .../commands/handlers/task-linked-issue-update.handler.ts | 0 packages/core/src/tasks/linked-issue/commands/index.ts | 2 ++ .../commands/task-linked-issue-create.command.ts | 8 ++++++++ .../commands/task-linked-issue-update.command.ts | 8 ++++++++ 6 files changed, 18 insertions(+) create mode 100644 packages/core/src/tasks/linked-issue/commands/handlers/index.ts create mode 100644 packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts create mode 100644 packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts create mode 100644 packages/core/src/tasks/linked-issue/commands/index.ts create mode 100644 packages/core/src/tasks/linked-issue/commands/task-linked-issue-create.command.ts create mode 100644 packages/core/src/tasks/linked-issue/commands/task-linked-issue-update.command.ts diff --git a/packages/core/src/tasks/linked-issue/commands/handlers/index.ts b/packages/core/src/tasks/linked-issue/commands/handlers/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts b/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts b/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/core/src/tasks/linked-issue/commands/index.ts b/packages/core/src/tasks/linked-issue/commands/index.ts new file mode 100644 index 00000000000..67caf1505cd --- /dev/null +++ b/packages/core/src/tasks/linked-issue/commands/index.ts @@ -0,0 +1,2 @@ +export * from './task-linked-issue-create.command'; +export * from './task-linked-issue-update.command'; diff --git a/packages/core/src/tasks/linked-issue/commands/task-linked-issue-create.command.ts b/packages/core/src/tasks/linked-issue/commands/task-linked-issue-create.command.ts new file mode 100644 index 00000000000..2b02682c765 --- /dev/null +++ b/packages/core/src/tasks/linked-issue/commands/task-linked-issue-create.command.ts @@ -0,0 +1,8 @@ +import { ICommand } from '@nestjs/cqrs'; +import { ITaskLinkedIssueCreateInput } from '@gauzy/contracts'; + +export class TaskLinkedIssueCreateCommand implements ICommand { + static readonly type = '[Task Linked Issue] Create'; + + constructor(public readonly input: ITaskLinkedIssueCreateInput) {} +} diff --git a/packages/core/src/tasks/linked-issue/commands/task-linked-issue-update.command.ts b/packages/core/src/tasks/linked-issue/commands/task-linked-issue-update.command.ts new file mode 100644 index 00000000000..732cf48b4f4 --- /dev/null +++ b/packages/core/src/tasks/linked-issue/commands/task-linked-issue-update.command.ts @@ -0,0 +1,8 @@ +import { ICommand } from '@nestjs/cqrs'; +import { ID, ITaskLinkedIssueUpdateInput } from '@gauzy/contracts'; + +export class TaskLinkedIssueUpdateCommand implements ICommand { + static readonly type = '[Task Linked Issue] Update'; + + constructor(public readonly id: ID, public readonly input: ITaskLinkedIssueUpdateInput) {} +} From 707d9a61481db5416c8f634506f086ffadc9f6a4 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 09:37:53 +0200 Subject: [PATCH 05/15] feat: add task linked issue command handlers --- .../tasks/linked-issue/commands/handlers/index.ts | 4 ++++ .../handlers/task-linked-issue-create.handler.ts | 15 +++++++++++++++ .../handlers/task-linked-issue-update.handler.ts | 15 +++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/packages/core/src/tasks/linked-issue/commands/handlers/index.ts b/packages/core/src/tasks/linked-issue/commands/handlers/index.ts index e69de29bb2d..d78cf9b943f 100644 --- a/packages/core/src/tasks/linked-issue/commands/handlers/index.ts +++ b/packages/core/src/tasks/linked-issue/commands/handlers/index.ts @@ -0,0 +1,4 @@ +import { TaskLinkedIssueCreateHandler } from './task-linked-issue-create.handler'; +import { TaskLinkedIssueUpdateHandler } from './task-linked-issue-update.handler'; + +export const CommandHandlers = [TaskLinkedIssueCreateHandler, TaskLinkedIssueUpdateHandler]; diff --git a/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts b/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts index e69de29bb2d..a5bb065c6b1 100644 --- a/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts +++ b/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-create.handler.ts @@ -0,0 +1,15 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { ITaskLinkedIssue } from '@gauzy/contracts'; +import { TaskLinkedIssueCreateCommand } from '../task-linked-issue-create.command'; +import { TaskLinkedIssueService } from '../../task-linked-issue.service'; + +@CommandHandler(TaskLinkedIssueCreateCommand) +export class TaskLinkedIssueCreateHandler implements ICommandHandler { + constructor(private readonly taskLinkedIssueService: TaskLinkedIssueService) {} + + public async execute(command: TaskLinkedIssueCreateCommand): Promise { + const { input } = command; + + return await this.taskLinkedIssueService.create(input); + } +} diff --git a/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts b/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts index e69de29bb2d..761631b5eac 100644 --- a/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts +++ b/packages/core/src/tasks/linked-issue/commands/handlers/task-linked-issue-update.handler.ts @@ -0,0 +1,15 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { ITaskLinkedIssue } from '@gauzy/contracts'; +import { TaskLinkedIssueUpdateCommand } from '../task-linked-issue-update.command'; +import { TaskLinkedIssueService } from '../../task-linked-issue.service'; + +@CommandHandler(TaskLinkedIssueUpdateCommand) +export class TaskLinkedIssueUpdateHandler implements ICommandHandler { + constructor(private readonly taskLinkedIssueService: TaskLinkedIssueService) {} + + public async execute(command: TaskLinkedIssueUpdateCommand): Promise { + const { id, input } = command; + + return await this.taskLinkedIssueService.update(id, input); + } +} From a313013a0195411e056779b73a2638b817214632 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 09:45:32 +0200 Subject: [PATCH 06/15] fix: improve task linked issue using commands --- .../task-linked-issue.controller.ts | 46 ++++++++++++++----- .../linked-issue/task-linked-issue.module.ts | 5 +- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index 7113aa0facd..b5d52294854 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -1,5 +1,6 @@ import { Body, Controller, HttpCode, HttpStatus, Param, Post, Put, UseGuards } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { CommandBus } from '@nestjs/cqrs'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ID, ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from '../../shared/guards'; import { UUIDValidationPipe, UseValidationPipe } from '../../shared/pipes'; @@ -8,37 +9,63 @@ import { CrudController } from '../../core/crud'; import { TaskLinkedIssue } from './task-linked-issue.entity'; import { TaskLinkedIssueService } from './task-linked-issue.service'; import { CreateTaskLinkedIssueDTO, UpdateTaskLinkedIssueDTO } from './dto'; +import { TaskLinkedIssueCreateCommand, TaskLinkedIssueUpdateCommand } from './commands'; @ApiTags('Linked Issue') @UseGuards(TenantPermissionGuard, PermissionGuard) @Permissions(PermissionsEnum.ALL_ORG_EDIT) @Controller() export class TaskLinkedIssueController extends CrudController { - constructor(protected readonly taskLinkedIssueService: TaskLinkedIssueService) { + constructor( + protected readonly taskLinkedIssueService: TaskLinkedIssueService, + private readonly commandBus: CommandBus + ) { super(taskLinkedIssueService); } /** * Create new Linked Issue * - * @param entity - * @returns + * @param entity - The input data for creating a task linked issue. */ + + @ApiOperation({ summary: 'Create Task Linked Issue' }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'The record has been successfully created.' + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid input, The response body may contain clues as to what went wrong' + }) @HttpCode(HttpStatus.CREATED) @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_ADD) @Post() @UseValidationPipe({ whitelist: true }) async create(@Body() entity: CreateTaskLinkedIssueDTO): Promise { - return await this.taskLinkedIssueService.create(entity); + return await this.commandBus.execute(new TaskLinkedIssueCreateCommand(entity)); } /** * Update existing Linked Issue * - * @param id - * @param entity + * @param id - The ID of the task linked issue to update. + * @param entity - The input data for updating the task linked issue. * @returns */ + @ApiOperation({ summary: 'Update an existing task linked issue' }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'The record has been successfully edited.' + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: 'Invalid input, The response body may contain clues as to what went wrong' + }) @HttpCode(HttpStatus.ACCEPTED) @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_EDIT) @Put(':id') @@ -47,9 +74,6 @@ export class TaskLinkedIssueController extends CrudController { @Param('id', UUIDValidationPipe) id: ID, @Body() entity: UpdateTaskLinkedIssueDTO ): Promise { - return await this.taskLinkedIssueService.create({ - ...entity, - id - }); + return await this.commandBus.execute(new TaskLinkedIssueUpdateCommand(id, entity)); } } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts index cf3098adf43..583c0ed116b 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts @@ -5,6 +5,7 @@ import { RouterModule } from '@nestjs/core'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { RolePermissionModule } from '../../role-permission/role-permission.module'; import { TaskLinkedIssue } from './task-linked-issue.entity'; +import { CommandHandlers } from './commands/handlers'; import { TaskLinkedIssueController } from './task-linked-issue.controller'; import { TaskLinkedIssueService } from './task-linked-issue.service'; @@ -17,7 +18,7 @@ import { TaskLinkedIssueService } from './task-linked-issue.service'; CqrsModule ], controllers: [TaskLinkedIssueController], - providers: [TaskLinkedIssueService], + providers: [TaskLinkedIssueService, ...CommandHandlers], exports: [TaskLinkedIssueService] }) -export class TaskLinkedIssueModule { } +export class TaskLinkedIssueModule {} From 8cd2630e3b0c10ea29f7f5678b207335397e6678 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 10:17:56 +0200 Subject: [PATCH 07/15] feat: task-linked issue delete log --- .../task-linked-issue.controller.ts | 18 +++++++- .../linked-issue/task-linked-issue.service.ts | 42 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index b5d52294854..21da1230230 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -1,6 +1,7 @@ -import { Body, Controller, HttpCode, HttpStatus, Param, Post, Put, UseGuards } from '@nestjs/common'; +import { Body, Controller, Delete, HttpCode, HttpStatus, Param, Post, Put, UseGuards } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { DeleteResult } from 'typeorm'; import { ID, ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from '../../shared/guards'; import { UUIDValidationPipe, UseValidationPipe } from '../../shared/pipes'; @@ -76,4 +77,19 @@ export class TaskLinkedIssueController extends CrudController { ): Promise { return await this.commandBus.execute(new TaskLinkedIssueUpdateCommand(id, entity)); } + + @ApiOperation({ summary: 'Delete Task Linked issue' }) + @ApiResponse({ + status: HttpStatus.NO_CONTENT, + description: 'The record has been successfully deleted' + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @HttpCode(HttpStatus.ACCEPTED) + @Delete('/:id') + async delete(@Param('id', UUIDValidationPipe) id: ID): Promise { + return await this.taskLinkedIssueService.delete(id); + } } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index e557c11f9c5..ae24ad538b2 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -1,5 +1,6 @@ import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; +import { DeleteResult, FindOneOptions } from 'typeorm'; import { ActionTypeEnum, ActorTypeEnum, @@ -110,4 +111,45 @@ export class TaskLinkedIssueService extends TenantAwareCrudService} [options] - Optional query options to find the task linked issue before deletion. + * @returns {Promise} The result of the deletion operation. + * @throws {HttpException} Throws a Bad Request exception if the deletion fails. + * @throws {NotFoundException} Throws a Not Found exception if the task linked issue does not exist. + * + */ + async delete(id: ID, options?: FindOneOptions): Promise { + const tenantId = RequestContext.currentTenantId(); + try { + // Retrieve existing task linked issue + const existingTaskLinkedIssue = await this.findOneByIdString(id); + + if (!existingTaskLinkedIssue) { + throw new NotFoundException('View not found'); + } + + // Generate deleted activity log + const { organizationId } = existingTaskLinkedIssue; + this.activityLogService.logActivity( + BaseEntityEnum.TaskLinkedIssue, + ActionTypeEnum.Deleted, + ActorTypeEnum.User, + id, + taskRelatedIssueRelationMap(existingTaskLinkedIssue.action), + existingTaskLinkedIssue, + organizationId, + tenantId + ); + + // + return await super.delete(id, options); + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to delete task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); + } + } } From b069f28ade026ea5cfbcaaf1a7a5dd85e76aa892 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 10:23:05 +0200 Subject: [PATCH 08/15] fix(typos): cspell error --- packages/core/src/tasks/task.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/tasks/task.service.ts b/packages/core/src/tasks/task.service.ts index 292208348c9..f6aed374fb4 100644 --- a/packages/core/src/tasks/task.service.ts +++ b/packages/core/src/tasks/task.service.ts @@ -89,7 +89,7 @@ export class TaskService extends TenantAwareCrudService { // Register Task Sprint moving history if (organizationSprintId && organizationSprintId !== task.organizationSprintId) { await this.typeOrmOrganizationSprintTaskHistoryRepository.save({ - fromSprintId: task.organizationSprintId || organizationSprintId, // Use incoming sprint ID if the task's organizationSprintId was priviously null or undefined + fromSprintId: task.organizationSprintId || organizationSprintId, // Use incoming sprint ID if the task's organizationSprintId was previously null or undefined toSprintId: organizationSprintId, taskId: updatedTask.id, movedById: userId, From 352c847cb8cbd7000f1cc5007bb183cab229be36 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 10:42:27 +0200 Subject: [PATCH 09/15] fix: improve code by rabbit suggestions --- .../src/tasks/linked-issue/task-linked-issue.helper.ts | 8 ++++++-- .../src/tasks/linked-issue/task-linked-issue.service.ts | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts index f8843b9871b..a362dcf90be 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts @@ -12,10 +12,14 @@ export function taskRelatedIssueRelationMap(relation: TaskRelatedIssuesRelationE [TaskRelatedIssuesRelationEnum.CLONES]: 'Clones', [TaskRelatedIssuesRelationEnum.DUPLICATES]: 'Duplicates', [TaskRelatedIssuesRelationEnum.IS_BLOCKED_BY]: 'Is Blocked By', - [TaskRelatedIssuesRelationEnum.IS_CLONED_BY]: 'Is cloned By', + [TaskRelatedIssuesRelationEnum.IS_CLONED_BY]: 'Is Cloned By', [TaskRelatedIssuesRelationEnum.IS_DUPLICATED_BY]: 'Is Duplicated By', [TaskRelatedIssuesRelationEnum.RELATES_TO]: 'Relates To' }; - return issueRelationMap[relation]; + const issueRelation = issueRelationMap[relation]; + if (!issueRelation) { + throw new Error(`Unsupported relation type: ${relation}`); + } + return issueRelation; } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index ae24ad538b2..39d442140a0 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -84,7 +84,7 @@ export class TaskLinkedIssueService extends TenantAwareCrudService Date: Thu, 31 Oct 2024 13:01:00 +0200 Subject: [PATCH 10/15] fix: task linked issue find all using pagination params --- .../task-linked-issue.controller.ts | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index 21da1230230..d475111973c 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -1,12 +1,24 @@ -import { Body, Controller, Delete, HttpCode, HttpStatus, Param, Post, Put, UseGuards } from '@nestjs/common'; +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Param, + Post, + Put, + Query, + UseGuards +} from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { DeleteResult } from 'typeorm'; -import { ID, ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; +import { ID, IPagination, ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; import { PermissionGuard, TenantPermissionGuard } from '../../shared/guards'; import { UUIDValidationPipe, UseValidationPipe } from '../../shared/pipes'; import { Permissions } from '../../shared/decorators'; -import { CrudController } from '../../core/crud'; +import { CrudController, PaginationParams } from '../../core/crud'; import { TaskLinkedIssue } from './task-linked-issue.entity'; import { TaskLinkedIssueService } from './task-linked-issue.service'; import { CreateTaskLinkedIssueDTO, UpdateTaskLinkedIssueDTO } from './dto'; @@ -24,6 +36,24 @@ export class TaskLinkedIssueController extends CrudController { super(taskLinkedIssueService); } + @ApiOperation({ + summary: 'Find all' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Found task linked issues', + type: TaskLinkedIssue + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Record not found' + }) + @Get() + @UseValidationPipe() + async findAll(@Query() params: PaginationParams): Promise> { + return await this.taskLinkedIssueService.findAll(params); + } + /** * Create new Linked Issue * From fdc32ec40c19c8f563020590015a9573370b589b Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Thu, 31 Oct 2024 14:04:38 +0200 Subject: [PATCH 11/15] feat: implement task linked issue soft delete --- .../task-linked-issue.controller.ts | 16 ++++++++ .../linked-issue/task-linked-issue.service.ts | 39 +++++++++++++++---- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index d475111973c..4d88849a9fd 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -122,4 +122,20 @@ export class TaskLinkedIssueController extends CrudController { async delete(@Param('id', UUIDValidationPipe) id: ID): Promise { return await this.taskLinkedIssueService.delete(id); } + + @ApiOperation({ summary: 'Soft delete Task Linked Issue record' }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'The record has been successfully soft-deleted' + }) + @ApiResponse({ + status: HttpStatus.NOT_FOUND, + description: 'Task Linked Issue record not found' + }) + @HttpCode(HttpStatus.ACCEPTED) + @Delete('/:id/soft') + @UseValidationPipe({ whitelist: true }) + async softRemove(@Param('id', UUIDValidationPipe) id: ID): Promise { + return await this.taskLinkedIssueService.softDelete(id); + } } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index 39d442140a0..24bc5d9d3f5 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -1,6 +1,6 @@ import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { DeleteResult, FindOneOptions } from 'typeorm'; +import { DeleteResult, FindOneOptions, UpdateResult } from 'typeorm'; import { ActionTypeEnum, ActorTypeEnum, @@ -123,6 +123,35 @@ export class TaskLinkedIssueService extends TenantAwareCrudService): Promise { + try { + await this.deleteActivityLog(id); + + return await super.delete(id, options); + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to delete task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); + } + } + + /** + * Soft deletes a task linked issue by its ID, preserving the data while marking it as deleted. + * + * @param {ID} id - The ID of the task linked issue to be soft deleted. + * @returns {Promise} - A promise that resolves to the soft-deleted task linked issue or the update result. + * @throws {HttpException} - Throws an error if the task linked issue cannot be found or the deletion process fails. + */ + async softDelete(id: ID): Promise { + try { + await this.deleteActivityLog(id); + + return await super.softDelete(id); + } catch (error) { + // Handle errors and return an appropriate error response + throw new HttpException(`Failed to delete task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); + } + } + + private async deleteActivityLog(id: ID) { const tenantId = RequestContext.currentTenantId(); try { // Retrieve existing task linked issue @@ -144,12 +173,6 @@ export class TaskLinkedIssueService extends TenantAwareCrudService Date: Tue, 5 Nov 2024 05:47:59 +0200 Subject: [PATCH 12/15] fix: rabbit suggestions --- .../src/tasks/linked-issue/task-linked-issue.controller.ts | 4 ++-- .../core/src/tasks/linked-issue/task-linked-issue.service.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index 4d88849a9fd..9d9a52ce615 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -118,7 +118,7 @@ export class TaskLinkedIssueController extends CrudController { description: 'Record not found' }) @HttpCode(HttpStatus.ACCEPTED) - @Delete('/:id') + @Delete(':id') async delete(@Param('id', UUIDValidationPipe) id: ID): Promise { return await this.taskLinkedIssueService.delete(id); } @@ -133,7 +133,7 @@ export class TaskLinkedIssueController extends CrudController { description: 'Task Linked Issue record not found' }) @HttpCode(HttpStatus.ACCEPTED) - @Delete('/:id/soft') + @Delete(':id/soft') @UseValidationPipe({ whitelist: true }) async softRemove(@Param('id', UUIDValidationPipe) id: ID): Promise { return await this.taskLinkedIssueService.softDelete(id); diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index 24bc5d9d3f5..d564f7d3032 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -173,6 +173,8 @@ export class TaskLinkedIssueService extends TenantAwareCrudService Date: Tue, 5 Nov 2024 11:14:34 +0530 Subject: [PATCH 13/15] fix: improved bit code --- .../task-linked-issue.controller.ts | 48 +++++++++++------ .../linked-issue/task-linked-issue.entity.ts | 8 +-- .../linked-issue/task-linked-issue.helper.ts | 27 +++++----- .../linked-issue/task-linked-issue.service.ts | 53 +++++++++---------- 4 files changed, 75 insertions(+), 61 deletions(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index 9d9a52ce615..2c194709d4d 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -15,10 +15,10 @@ import { CommandBus } from '@nestjs/cqrs'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { DeleteResult } from 'typeorm'; import { ID, IPagination, ITaskLinkedIssue, PermissionsEnum } from '@gauzy/contracts'; +import { CrudController, PaginationParams } from '../../core/crud'; import { PermissionGuard, TenantPermissionGuard } from '../../shared/guards'; import { UUIDValidationPipe, UseValidationPipe } from '../../shared/pipes'; import { Permissions } from '../../shared/decorators'; -import { CrudController, PaginationParams } from '../../core/crud'; import { TaskLinkedIssue } from './task-linked-issue.entity'; import { TaskLinkedIssueService } from './task-linked-issue.service'; import { CreateTaskLinkedIssueDTO, UpdateTaskLinkedIssueDTO } from './dto'; @@ -26,16 +26,22 @@ import { TaskLinkedIssueCreateCommand, TaskLinkedIssueUpdateCommand } from './co @ApiTags('Linked Issue') @UseGuards(TenantPermissionGuard, PermissionGuard) -@Permissions(PermissionsEnum.ALL_ORG_EDIT) +@Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_EDIT) @Controller() export class TaskLinkedIssueController extends CrudController { constructor( - protected readonly taskLinkedIssueService: TaskLinkedIssueService, + private readonly taskLinkedIssueService: TaskLinkedIssueService, private readonly commandBus: CommandBus ) { super(taskLinkedIssueService); } + /** + * Finds all task linked issues based on the provided query parameters. + * + * @param params - The pagination and filter parameters for the query. + * @returns A promise that resolves to a paginated list of task linked issues. + */ @ApiOperation({ summary: 'Find all' }) @@ -51,15 +57,15 @@ export class TaskLinkedIssueController extends CrudController { @Get() @UseValidationPipe() async findAll(@Query() params: PaginationParams): Promise> { - return await this.taskLinkedIssueService.findAll(params); + return this.taskLinkedIssueService.findAll(params); } /** - * Create new Linked Issue + * Creates a new task linked issue. * * @param entity - The input data for creating a task linked issue. + * @returns A promise that resolves to the created task linked issue. */ - @ApiOperation({ summary: 'Create Task Linked Issue' }) @ApiResponse({ status: HttpStatus.CREATED, @@ -67,22 +73,22 @@ export class TaskLinkedIssueController extends CrudController { }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, - description: 'Invalid input, The response body may contain clues as to what went wrong' + description: 'Invalid input. The response body may contain clues as to what went wrong.' }) @HttpCode(HttpStatus.CREATED) @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_ADD) @Post() @UseValidationPipe({ whitelist: true }) async create(@Body() entity: CreateTaskLinkedIssueDTO): Promise { - return await this.commandBus.execute(new TaskLinkedIssueCreateCommand(entity)); + return this.commandBus.execute(new TaskLinkedIssueCreateCommand(entity)); } /** - * Update existing Linked Issue + * Updates an existing task linked issue. * * @param id - The ID of the task linked issue to update. * @param entity - The input data for updating the task linked issue. - * @returns + * @returns A promise that resolves to the updated task linked issue. */ @ApiOperation({ summary: 'Update an existing task linked issue' }) @ApiResponse({ @@ -95,7 +101,7 @@ export class TaskLinkedIssueController extends CrudController { }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, - description: 'Invalid input, The response body may contain clues as to what went wrong' + description: 'Invalid input. The response body may contain clues as to what went wrong.' }) @HttpCode(HttpStatus.ACCEPTED) @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_EDIT) @@ -105,10 +111,16 @@ export class TaskLinkedIssueController extends CrudController { @Param('id', UUIDValidationPipe) id: ID, @Body() entity: UpdateTaskLinkedIssueDTO ): Promise { - return await this.commandBus.execute(new TaskLinkedIssueUpdateCommand(id, entity)); + return this.commandBus.execute(new TaskLinkedIssueUpdateCommand(id, entity)); } - @ApiOperation({ summary: 'Delete Task Linked issue' }) + /** + * Deletes a task linked issue. + * + * @param id - The ID of the task linked issue to delete. + * @returns A promise that resolves to the result of the delete operation. + */ + @ApiOperation({ summary: 'Delete Task Linked Issue' }) @ApiResponse({ status: HttpStatus.NO_CONTENT, description: 'The record has been successfully deleted' @@ -120,9 +132,15 @@ export class TaskLinkedIssueController extends CrudController { @HttpCode(HttpStatus.ACCEPTED) @Delete(':id') async delete(@Param('id', UUIDValidationPipe) id: ID): Promise { - return await this.taskLinkedIssueService.delete(id); + return this.taskLinkedIssueService.delete(id); } + /** + * Soft deletes a task linked issue record. + * + * @param id - The ID of the task linked issue to soft delete. + * @returns A promise that resolves to the result of the soft delete operation. + */ @ApiOperation({ summary: 'Soft delete Task Linked Issue record' }) @ApiResponse({ status: HttpStatus.OK, @@ -136,6 +154,6 @@ export class TaskLinkedIssueController extends CrudController { @Delete(':id/soft') @UseValidationPipe({ whitelist: true }) async softRemove(@Param('id', UUIDValidationPipe) id: ID): Promise { - return await this.taskLinkedIssueService.softDelete(id); + return this.taskLinkedIssueService.softDelete(id); } } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts index c971d2b57cb..9685d0e1ae3 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.entity.ts @@ -1,6 +1,6 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { JoinColumn, RelationId } from 'typeorm'; -import { IsEnum, IsUUID } from 'class-validator'; +import { IsEnum, IsOptional, IsUUID } from 'class-validator'; import { ID, ITask, ITaskLinkedIssue, TaskRelatedIssuesRelationEnum } from '@gauzy/contracts'; import { Task } from './../task.entity'; import { TenantOrganizationBaseEntity } from './../../core/entities/internal'; @@ -9,9 +9,9 @@ import { MikroOrmTaskLinkedIssueRepository } from './repository/mikro-orm-linked @MultiORMEntity('task_linked_issues', { mikroOrmRepository: () => MikroOrmTaskLinkedIssueRepository }) export class TaskLinkedIssue extends TenantOrganizationBaseEntity implements ITaskLinkedIssue { - @ApiProperty({ type: () => String, enum: TaskRelatedIssuesRelationEnum }) - @MultiORMColumn() + @ApiProperty({ enum: TaskRelatedIssuesRelationEnum }) @IsEnum(TaskRelatedIssuesRelationEnum) + @MultiORMColumn() action: TaskRelatedIssuesRelationEnum; /* @@ -20,6 +20,7 @@ export class TaskLinkedIssue extends TenantOrganizationBaseEntity implements ITa |-------------------------------------------------------------------------- */ @ApiPropertyOptional({ type: () => Task }) + @IsOptional() @MultiORMManyToOne(() => Task) @JoinColumn() taskFrom?: ITask; @@ -35,6 +36,7 @@ export class TaskLinkedIssue extends TenantOrganizationBaseEntity implements ITa * Task Linked Issues */ @ApiPropertyOptional({ type: () => Object }) + @IsOptional() @MultiORMManyToOne(() => Task, (it) => it.linkedIssues) @JoinColumn() taskTo?: ITask; diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts index a362dcf90be..d869a37ce97 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.helper.ts @@ -5,21 +5,20 @@ import { TaskRelatedIssuesRelationEnum } from '@gauzy/contracts'; * * @param {TaskRelatedIssuesRelationEnum} relation - The relation type from the enum `TaskRelatedIssuesRelationEnum`. * @returns {string} The corresponding string description for the given relation type. + * @throws {Error} If the relation type is unsupported. */ export function taskRelatedIssueRelationMap(relation: TaskRelatedIssuesRelationEnum): string { - const issueRelationMap: { [key in TaskRelatedIssuesRelationEnum]: string } = { - [TaskRelatedIssuesRelationEnum.BLOCKS]: 'Blocks', - [TaskRelatedIssuesRelationEnum.CLONES]: 'Clones', - [TaskRelatedIssuesRelationEnum.DUPLICATES]: 'Duplicates', - [TaskRelatedIssuesRelationEnum.IS_BLOCKED_BY]: 'Is Blocked By', - [TaskRelatedIssuesRelationEnum.IS_CLONED_BY]: 'Is Cloned By', - [TaskRelatedIssuesRelationEnum.IS_DUPLICATED_BY]: 'Is Duplicated By', - [TaskRelatedIssuesRelationEnum.RELATES_TO]: 'Relates To' - }; + const issueRelationMap = { + [TaskRelatedIssuesRelationEnum.BLOCKS]: 'Blocks', + [TaskRelatedIssuesRelationEnum.CLONES]: 'Clones', + [TaskRelatedIssuesRelationEnum.DUPLICATES]: 'Duplicates', + [TaskRelatedIssuesRelationEnum.IS_BLOCKED_BY]: 'Is Blocked By', + [TaskRelatedIssuesRelationEnum.IS_CLONED_BY]: 'Is Cloned By', + [TaskRelatedIssuesRelationEnum.IS_DUPLICATED_BY]: 'Is Duplicated By', + [TaskRelatedIssuesRelationEnum.RELATES_TO]: 'Relates To' + } as const; - const issueRelation = issueRelationMap[relation]; - if (!issueRelation) { - throw new Error(`Unsupported relation type: ${relation}`); - } - return issueRelation; + return issueRelationMap[relation] ?? (() => { + throw new Error(`Unsupported relation type: ${relation}`); + })(); } diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts index d564f7d3032..6ca88ce08e6 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.service.ts @@ -1,5 +1,4 @@ import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; import { DeleteResult, FindOneOptions, UpdateResult } from 'typeorm'; import { ActionTypeEnum, @@ -21,11 +20,8 @@ import { taskRelatedIssueRelationMap } from './task-linked-issue.helper'; @Injectable() export class TaskLinkedIssueService extends TenantAwareCrudService { constructor( - @InjectRepository(TaskLinkedIssue) typeOrmTaskLinkedIssueRepository: TypeOrmTaskLinkedIssueRepository, - mikroOrmTaskLinkedIssueRepository: MikroOrmTaskLinkedIssueRepository, - private readonly activityLogService: ActivityLogService ) { super(typeOrmTaskLinkedIssueRepository, mikroOrmTaskLinkedIssueRepository); @@ -113,53 +109,52 @@ export class TaskLinkedIssueService extends TenantAwareCrudService} [options] - Optional query options to find the task linked issue before deletion. - * @returns {Promise} The result of the deletion operation. - * @throws {HttpException} Throws a Bad Request exception if the deletion fails. - * @throws {NotFoundException} Throws a Not Found exception if the task linked issue does not exist. + * Deletes a task linked issue and logs the deletion activity. * + * @param id - The ID of the task linked issue to delete. + * @param options - Optional find options for the task linked issue. + * @returns A promise that resolves to the result of the delete operation. */ async delete(id: ID, options?: FindOneOptions): Promise { try { await this.deleteActivityLog(id); - - return await super.delete(id, options); + return super.delete(id, options); } catch (error) { - // Handle errors and return an appropriate error response + console.error(`Failed to delete task linked issue (ID: ${id}):`, error); throw new HttpException(`Failed to delete task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); } } /** - * Soft deletes a task linked issue by its ID, preserving the data while marking it as deleted. + * Soft deletes a task linked issue and logs the deletion activity. * - * @param {ID} id - The ID of the task linked issue to be soft deleted. - * @returns {Promise} - A promise that resolves to the soft-deleted task linked issue or the update result. - * @throws {HttpException} - Throws an error if the task linked issue cannot be found or the deletion process fails. + * @param id - The ID of the task linked issue to soft delete. + * @returns A promise that resolves to the result of the soft delete operation or the deleted entity. */ async softDelete(id: ID): Promise { try { await this.deleteActivityLog(id); - - return await super.softDelete(id); + return super.softDelete(id); } catch (error) { - // Handle errors and return an appropriate error response - throw new HttpException(`Failed to delete task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); + console.error(`Failed to soft delete task linked issue (ID: ${id}):`, error); + throw new HttpException(`Failed to soft delete task linked issue: ${error.message}`, HttpStatus.BAD_REQUEST); } } + /** + * Deletes an activity log for a given task linked issue. + * + * @param id - The ID of the task linked issue to delete. + */ private async deleteActivityLog(id: ID) { const tenantId = RequestContext.currentTenantId(); - try { - // Retrieve existing task linked issue - const existingTaskLinkedIssue = await this.findOneByIdString(id); - if (!existingTaskLinkedIssue) { - throw new NotFoundException('Task linked issue not found'); - } + try { + // Retrieve existing task linked issue + const existingTaskLinkedIssue = await this.findOneByIdString(id); + if (!existingTaskLinkedIssue) { + throw new NotFoundException('Task linked issue not found'); + } // Generate deleted activity log const { organizationId } = existingTaskLinkedIssue; @@ -174,7 +169,7 @@ export class TaskLinkedIssueService extends TenantAwareCrudService Date: Tue, 5 Nov 2024 11:48:28 +0530 Subject: [PATCH 14/15] fix: Nest can't resolve dependencies --- .../core/src/tasks/linked-issue/task-linked-issue.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts index 583c0ed116b..fda06ec6a8c 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.module.ts @@ -8,6 +8,7 @@ import { TaskLinkedIssue } from './task-linked-issue.entity'; import { CommandHandlers } from './commands/handlers'; import { TaskLinkedIssueController } from './task-linked-issue.controller'; import { TaskLinkedIssueService } from './task-linked-issue.service'; +import { TypeOrmTaskLinkedIssueRepository } from './repository/type-orm-linked-issue.repository'; @Module({ imports: [ @@ -18,7 +19,7 @@ import { TaskLinkedIssueService } from './task-linked-issue.service'; CqrsModule ], controllers: [TaskLinkedIssueController], - providers: [TaskLinkedIssueService, ...CommandHandlers], + providers: [TaskLinkedIssueService, TypeOrmTaskLinkedIssueRepository, ...CommandHandlers], exports: [TaskLinkedIssueService] }) export class TaskLinkedIssueModule {} From 25e8828c13553a5a47e4b103b9ae00bfa1c18817 Mon Sep 17 00:00:00 2001 From: GloireMutaliko21 Date: Tue, 5 Nov 2024 08:38:52 +0200 Subject: [PATCH 15/15] fix: task linked issue missing permissions --- .../src/tasks/linked-issue/task-linked-issue.controller.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts index 2c194709d4d..ad05dae1438 100644 --- a/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts +++ b/packages/core/src/tasks/linked-issue/task-linked-issue.controller.ts @@ -54,6 +54,7 @@ export class TaskLinkedIssueController extends CrudController { status: HttpStatus.NOT_FOUND, description: 'Record not found' }) + @Permissions(PermissionsEnum.ALL_ORG_VIEW, PermissionsEnum.ORG_TASK_VIEW) @Get() @UseValidationPipe() async findAll(@Query() params: PaginationParams): Promise> { @@ -130,6 +131,7 @@ export class TaskLinkedIssueController extends CrudController { description: 'Record not found' }) @HttpCode(HttpStatus.ACCEPTED) + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_DELETE) @Delete(':id') async delete(@Param('id', UUIDValidationPipe) id: ID): Promise { return this.taskLinkedIssueService.delete(id); @@ -151,6 +153,7 @@ export class TaskLinkedIssueController extends CrudController { description: 'Task Linked Issue record not found' }) @HttpCode(HttpStatus.ACCEPTED) + @Permissions(PermissionsEnum.ALL_ORG_EDIT, PermissionsEnum.ORG_TASK_DELETE) @Delete(':id/soft') @UseValidationPipe({ whitelist: true }) async softRemove(@Param('id', UUIDValidationPipe) id: ID): Promise {