From f8709a75cd1896a728820d395ac4dd4338dccb13 Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Mon, 18 May 2026 16:19:18 -0300 Subject: [PATCH 1/7] refactor: add agent field with id and agent name --- .../ticket/domain/entities/ticket.entity.ts | 8 +++ .../repositories/ticket.mongodb.repository.ts | 49 +++++++++++++++++-- .../infra/schemas/ticket.mongo.schema.ts | 2 +- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/backend/src/modules/ticket/domain/entities/ticket.entity.ts b/backend/src/modules/ticket/domain/entities/ticket.entity.ts index bdc7e78..c4a1bd3 100644 --- a/backend/src/modules/ticket/domain/entities/ticket.entity.ts +++ b/backend/src/modules/ticket/domain/entities/ticket.entity.ts @@ -46,6 +46,11 @@ export type TicketHistoryEntry = { occurredAt: Date; }; +export type AgentField = { + id: string; + name: string; +}; + export class Ticket { // Strutucture definition private _id: string; @@ -136,6 +141,7 @@ export class Ticket { status: TicketStatus; clientId: string; agentId?: string; + // agent?: AgentField; groupId?: string; escalationLevel: number; history: TicketHistoryEntry[]; @@ -152,7 +158,9 @@ export class Ticket { ticket._id = props._id; + // ticket._agentId = props.agent?.id ?? null; ticket._agentId = props.agentId ?? null; + ticket._groupId = props.groupId ?? null; ticket.attachmentsUrls = props.fileUrls ?? []; diff --git a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts index bdf6325..0a07c82 100644 --- a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts +++ b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts @@ -1,4 +1,4 @@ -import { Model, QueryFilter } from 'mongoose'; +import { Model } from 'mongoose'; import { Ticket } from '../../domain/entities/ticket.entity'; import { ITicketRepository } from '../../domain/repository/ticket.repository.interface'; import { TicketLean, TicketSchemaClass } from '../schemas/ticket.mongo.schema'; @@ -43,20 +43,59 @@ export class TicketMongoRepository extends ITicketRepository { agentId?: string; categories?: string[]; }): Promise { - let query: QueryFilter = {}; + let matchStage: Record = {}; if (filters?.agentId) { - query = { + matchStage = { $or: [ { agentId: filters.agentId }, { category: { $in: filters.categories ?? [] }, agentId: null }, ], }; } else if (filters?.clientId) { - query = { clientId: filters.clientId }; + matchStage = { clientId: filters.clientId }; } - const tickets = await this.ticketModel.find(query).exec(); + // const tickets = await this.ticketModel.find(query).exec(); + + const tickets = await this.ticketModel.aggregate([ + { $match: matchStage }, + { + $lookup: { + from: 'users', + let: { agentId: '$agentId' }, + pipeline: [ + { + $match: { + $expr: { + $and: [ + { $ne: ['$$agentId', null] }, + { $eq: [{ $toString: '$_id' }, '$$agentId'] }, + ], + }, + }, + }, + ], + as: 'agentData', + }, + }, + { + $addFields: { + agent: { + $cond: { + if: { $gt: [{ $size: '$agentData' }, 0] }, + then: { + id: { $toString: { $arrayElemAt: ['$agentData._id', 0] } }, + name: { $arrayElemAt: ['$agentData.name', 0] }, + }, + else: null, + }, + }, + }, + }, + { $unset: ['agentData', 'agentId', '__v'] }, + ]); + return tickets.map((t) => TicketMapper.toDomain(t)); } diff --git a/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts b/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts index 17ca095..39140bd 100644 --- a/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts +++ b/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts @@ -24,7 +24,7 @@ export class TicketHistoryEntrySchema { @Prop({ type: String, default: null }) solution: string | null; - + @Prop({ type: String, required: false }) attachmentUrl?: string; From 66afd6cdafab79aa3fde0799ddd01ff78bb7473a Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Mon, 18 May 2026 16:37:10 -0300 Subject: [PATCH 2/7] refactor: read all use case now returns agent field --- .../application/useCases/readAll/readAll.usecase.ts | 3 +-- .../modules/ticket/domain/entities/ticket.entity.ts | 6 ++++-- .../modules/ticket/infra/mappers/ticket.mapper.ts | 12 +++++++++--- .../ticket/infra/schemas/ticket.mongo.schema.ts | 5 +++++ 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts index 8462df7..28b7c01 100644 --- a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts +++ b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts @@ -15,7 +15,6 @@ export interface ReadAllTicketOutput { description: string; clientId: string; status: TicketStatus; - agentId: string | null; escalationLevel: number; createdAt: Date; updatedAt: Date | null; @@ -39,6 +38,7 @@ export class ReadAllTicketUseCase { : undefined; const foundedTickets = await this.repository.readAll({ ...filters }); + console.log('Founded tickets:', foundedTickets); const convertedTickets = foundedTickets.map((t: Ticket) => { const primitive = t.toPrimitives(); @@ -51,7 +51,6 @@ export class ReadAllTicketUseCase { description: primitive.description, clientId: primitive.clientId, status: primitive.status, - agentId: primitive.agentId, escalationLevel: primitive.escalationLevel, createdAt: primitive.createdAt, updatedAt: primitive.updatedAt, diff --git a/backend/src/modules/ticket/domain/entities/ticket.entity.ts b/backend/src/modules/ticket/domain/entities/ticket.entity.ts index c4a1bd3..4145550 100644 --- a/backend/src/modules/ticket/domain/entities/ticket.entity.ts +++ b/backend/src/modules/ticket/domain/entities/ticket.entity.ts @@ -57,6 +57,8 @@ export class Ticket { private _status: TicketStatus = TicketStatus.OPEN; private _agentId: string | null = null; + private agent: AgentField | null = null; + private _groupId: string | null = null; private escalationLevel: number = 1; private attachmentsUrls: string[] = []; @@ -141,7 +143,7 @@ export class Ticket { status: TicketStatus; clientId: string; agentId?: string; - // agent?: AgentField; + agent?: AgentField | null; groupId?: string; escalationLevel: number; history: TicketHistoryEntry[]; @@ -158,8 +160,8 @@ export class Ticket { ticket._id = props._id; - // ticket._agentId = props.agent?.id ?? null; ticket._agentId = props.agentId ?? null; + ticket.agent = props.agent ?? null; ticket._groupId = props.groupId ?? null; ticket.attachmentsUrls = props.fileUrls ?? []; diff --git a/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts b/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts index b2c60d5..053a331 100644 --- a/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts +++ b/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts @@ -6,10 +6,16 @@ import { TicketStatus, TicketHistoryEntry, } from '../../domain/entities/ticket.entity'; -import { TicketDocument, TicketLean } from '../schemas/ticket.mongo.schema'; +import { + TicketAggregate, + TicketDocument, + TicketLean, +} from '../schemas/ticket.mongo.schema'; export class TicketMapper { - static toDomain(doc: TicketDocument | TicketLean): Ticket { + static toDomain(doc: TicketDocument | TicketLean | TicketAggregate): Ticket { + const aggregated = doc as TicketAggregate; + return Ticket.restore({ _id: doc._id.toString(), title: doc.title, @@ -18,7 +24,7 @@ export class TicketMapper { status: doc.status as TicketStatus, description: doc.description, clientId: doc.clientId, - agentId: doc.agentId ?? undefined, + agent: aggregated.agent ?? null, escalationLevel: doc.escalationLevel, fileUrls: doc.attachmentsUrls, history: doc.history.map( diff --git a/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts b/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts index 39140bd..49525c6 100644 --- a/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts +++ b/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts @@ -2,6 +2,7 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { HydratedDocument } from 'mongoose'; import { + AgentField, TicketEventMessage, TicketEvents, TicketPriority, @@ -90,3 +91,7 @@ export type TicketDocument = HydratedDocument; export type TicketLean = TicketSchemaClass & { _id: string }; export const TicketSchema = SchemaFactory.createForClass(TicketSchemaClass); + +export type TicketAggregate = TicketLean & { + agent: AgentField | null; +}; From b90405f27126a18f75345ba2dfa3aee45e83652b Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Mon, 18 May 2026 16:47:32 -0300 Subject: [PATCH 3/7] fix: field agent doesnt sent in get response --- .../application/useCases/readAll/readAll.usecase.ts | 6 ++++-- .../modules/ticket/domain/entities/ticket.entity.ts | 1 + .../presentation/controllers/ticket.controller.ts | 12 ++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts index 28b7c01..0350651 100644 --- a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts +++ b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { - Ticket, + AgentField, TicketPriority, TicketStatus, } from '../../../domain/entities/ticket.entity'; @@ -19,6 +19,7 @@ export interface ReadAllTicketOutput { createdAt: Date; updatedAt: Date | null; closedAt: Date | null; + agent?: AgentField | null; } @Injectable() @@ -40,7 +41,7 @@ export class ReadAllTicketUseCase { const foundedTickets = await this.repository.readAll({ ...filters }); console.log('Founded tickets:', foundedTickets); - const convertedTickets = foundedTickets.map((t: Ticket) => { + const convertedTickets = foundedTickets.map((t) => { const primitive = t.toPrimitives(); return { @@ -55,6 +56,7 @@ export class ReadAllTicketUseCase { createdAt: primitive.createdAt, updatedAt: primitive.updatedAt, closedAt: primitive.closedAt, + agent: primitive.agent, }; }); diff --git a/backend/src/modules/ticket/domain/entities/ticket.entity.ts b/backend/src/modules/ticket/domain/entities/ticket.entity.ts index 4145550..7df0ed1 100644 --- a/backend/src/modules/ticket/domain/entities/ticket.entity.ts +++ b/backend/src/modules/ticket/domain/entities/ticket.entity.ts @@ -190,6 +190,7 @@ export class Ticket { fileUrls: this.attachmentsUrls, status: this.status, agentId: this._agentId, + agent: this.agent ? { ...this.agent } : null, groupId: this._groupId, escalationLevel: this.escalationLevel, history: this.history, diff --git a/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts b/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts index 2811ac3..0769349 100644 --- a/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts +++ b/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts @@ -43,7 +43,10 @@ import { Roles } from '../../../auth/guards/roles.decorator'; import { UserRole } from '../../../shared/enums/user.enum'; import { GetHistoryFilteredUseCase } from '../../application/useCases/getHistoryFiltered/getHistoryFiltered.usecase'; import { GetHistoryFiltersRequest } from '../dtos/getHistory.dto'; -import { TicketEvents, TicketStatus } from '../../domain/entities/ticket.entity'; +import { + TicketEvents, + TicketStatus, +} from '../../domain/entities/ticket.entity'; @ApiTags('Ticket') @Controller('tickets') @@ -59,7 +62,7 @@ export class TicketController { private readonly newAgentUseCase: NewAgentTicketUseCase, private readonly deleteUseCase: DeleteTicketUseCase, private readonly closeUseCase: CloseTicketUseCase, - ) { } + ) {} @Post() @ApiOperation({ summary: 'Cria um ticket' }) @@ -195,10 +198,7 @@ export class TicketController { @ApiParam({ name: 'id', example: 'uuid-do-ticket' }) @ApiBody({ type: CloseTicketRequest }) @ApiResponse({ status: 200, description: 'Ticket fechado com sucesso.' }) - async closeTicket( - @Param('id') id: string, - @Body() body: CloseTicketRequest, - ) { + async closeTicket(@Param('id') id: string, @Body() body: CloseTicketRequest) { const data = TicketMapper.toCloseTicketInput(id, body); const response = await this.closeUseCase.execute(data); From fd3e903a62375465331b794f24f00dec8484ed1d Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Wed, 20 May 2026 22:32:52 -0300 Subject: [PATCH 4/7] refactor: separate aggregate helper and aggregate category and client --- .../ticket/domain/entities/ticket.entity.ts | 13 +- .../ticket.mongodb.repository.int.spec.ts | 2 +- .../infra/helpers/ticket.aggregate.builder.ts | 130 ++++++++++++++++++ .../ticket/infra/mappers/ticket.mapper.ts | 2 + .../repositories/ticket.mongodb.repository.ts | 56 ++------ .../infra/schemas/ticket.mongo.schema.ts | 2 + 6 files changed, 161 insertions(+), 44 deletions(-) create mode 100644 backend/src/modules/ticket/infra/helpers/ticket.aggregate.builder.ts diff --git a/backend/src/modules/ticket/domain/entities/ticket.entity.ts b/backend/src/modules/ticket/domain/entities/ticket.entity.ts index 7df0ed1..5ce5c42 100644 --- a/backend/src/modules/ticket/domain/entities/ticket.entity.ts +++ b/backend/src/modules/ticket/domain/entities/ticket.entity.ts @@ -47,10 +47,16 @@ export type TicketHistoryEntry = { }; export type AgentField = { - id: string; + id: string | null; name: string; }; +export type ClientField = { + id: string | null; + name: string; +}; + + export class Ticket { // Strutucture definition private _id: string; @@ -58,6 +64,7 @@ export class Ticket { private _status: TicketStatus = TicketStatus.OPEN; private _agentId: string | null = null; private agent: AgentField | null = null; + private client: ClientField | null = null; private _groupId: string | null = null; private escalationLevel: number = 1; @@ -142,7 +149,8 @@ export class Ticket { fileUrls?: string[]; status: TicketStatus; clientId: string; - agentId?: string; + client?: ClientField | null; + agentId?: string | null; agent?: AgentField | null; groupId?: string; escalationLevel: number; @@ -162,6 +170,7 @@ export class Ticket { ticket._agentId = props.agentId ?? null; ticket.agent = props.agent ?? null; + ticket.client = props.client ?? null; ticket._groupId = props.groupId ?? null; ticket.attachmentsUrls = props.fileUrls ?? []; diff --git a/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts b/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts index 5582b84..f1db44d 100644 --- a/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts +++ b/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts @@ -148,7 +148,7 @@ describe('ITicketRepository', () => { const ticket = await repository.create(ticketToCreate); expect(ticket).toBeDefined(); - expect(ticket.agentId).toBe(null); + expect(ticket.agent).toBeNull(); expect(ticket.status).toBe(TicketStatus.OPEN); const newAgentId = randomUUID(); diff --git a/backend/src/modules/ticket/infra/helpers/ticket.aggregate.builder.ts b/backend/src/modules/ticket/infra/helpers/ticket.aggregate.builder.ts new file mode 100644 index 0000000..a659773 --- /dev/null +++ b/backend/src/modules/ticket/infra/helpers/ticket.aggregate.builder.ts @@ -0,0 +1,130 @@ +export class TicketAggregateBuilder { + static agentLookup() { + return [ + { + $lookup: { + from: 'users', + let: { agentId: '$agentId' }, + pipeline: [ + { + $match: { + $expr: { + $and: [ + { $ne: ['$$agentId', null] }, + { $eq: [{ $toString: '$_id' }, '$$agentId'] }, + ], + }, + }, + }, + ], + as: 'agentData', + }, + }, + { + $addFields: { + agent: { + $cond: { + if: { $gt: [{ $size: '$agentData' }, 0] }, + then: { + id: { $toString: { $arrayElemAt: ['$agentData._id', 0] } }, + name: { $arrayElemAt: ['$agentData.name', 0] }, + }, + else: null, + }, + }, + }, + }, + ]; + } + + static categoryLookup() { + return [ + { + $lookup: { + from: 'categories', + let: { categoryId: '$category' }, + pipeline: [ + { + $match: { + $expr: { + $and: [ + { $ne: ['$$categoryId', null] }, + { $eq: [{ $toString: '$_id' }, '$$categoryId'] }, + ], + }, + }, + }, + ], + as: 'categoryData', + }, + }, + { + $addFields: { + category: { + $cond: { + if: { $gt: [{ $size: '$categoryData' }, 0] }, + then: { + id: { $toString: { $arrayElemAt: ['$categoryData._id', 0] } }, + name: { $arrayElemAt: ['$categoryData.name', 0] }, + }, + else: null, + }, + }, + }, + }, + ]; + } + + static clientLookup() { + return [ + { + $lookup: { + from: 'users', + let: { id: '$clientId' }, + pipeline: [ + { + $match: { + $expr: { + $and: [ + { $ne: ['$$id', null] }, + { $eq: [{ $toString: '$_id' }, '$$id'] }, + ], + }, + }, + }, + ], + as: 'clientData', + }, + }, + { + $addFields: { + client: { + $cond: { + if: { $gt: [{ $size: '$clientData' }, 0] }, + then: { + id: { $toString: { $arrayElemAt: ['$clientData._id', 0] } }, + name: { $arrayElemAt: ['$clientData.name', 0] }, + }, + else: null, + }, + }, + }, + }, + ]; + } + + static cleanup() { + return { + $unset: ['agentData', 'agentId', 'categoryData', '__v'], + }; + } + + static buildAggregate() { + return [ + ...this.agentLookup(), + ...this.categoryLookup(), + ...this.clientLookup(), + this.cleanup(), + ]; + } +} diff --git a/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts b/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts index 053a331..8f49ec5 100644 --- a/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts +++ b/backend/src/modules/ticket/infra/mappers/ticket.mapper.ts @@ -24,6 +24,8 @@ export class TicketMapper { status: doc.status as TicketStatus, description: doc.description, clientId: doc.clientId, + client: aggregated.client ?? null, + agentId: doc.agentId ?? null, agent: aggregated.agent ?? null, escalationLevel: doc.escalationLevel, fileUrls: doc.attachmentsUrls, diff --git a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts index 0a07c82..249a554 100644 --- a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts +++ b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts @@ -4,6 +4,8 @@ import { ITicketRepository } from '../../domain/repository/ticket.repository.int import { TicketLean, TicketSchemaClass } from '../schemas/ticket.mongo.schema'; import { InjectModel } from '@nestjs/mongoose'; import { TicketMapper } from '../mappers/ticket.mapper'; +import { TicketAggregateBuilder } from '../helpers/ticket.aggregate.builder'; +// import TicketAggregateBuilder from '../helpers/ticket.aggregate.builder'; export class TicketMongoRepository extends ITicketRepository { constructor( @@ -56,58 +58,30 @@ export class TicketMongoRepository extends ITicketRepository { matchStage = { clientId: filters.clientId }; } - // const tickets = await this.ticketModel.find(query).exec(); - const tickets = await this.ticketModel.aggregate([ { $match: matchStage }, - { - $lookup: { - from: 'users', - let: { agentId: '$agentId' }, - pipeline: [ - { - $match: { - $expr: { - $and: [ - { $ne: ['$$agentId', null] }, - { $eq: [{ $toString: '$_id' }, '$$agentId'] }, - ], - }, - }, - }, - ], - as: 'agentData', - }, - }, - { - $addFields: { - agent: { - $cond: { - if: { $gt: [{ $size: '$agentData' }, 0] }, - then: { - id: { $toString: { $arrayElemAt: ['$agentData._id', 0] } }, - name: { $arrayElemAt: ['$agentData.name', 0] }, - }, - else: null, - }, - }, - }, - }, - { $unset: ['agentData', 'agentId', '__v'] }, + ...TicketAggregateBuilder.buildAggregate(), ]); return tickets.map((t) => TicketMapper.toDomain(t)); } async readById(id: string): Promise { - const foundedTicket = await this.ticketModel - .findById(id) - .lean() + const result = await this.ticketModel + .aggregate([ + { $match: { _id: id } }, + { $limit: 1 }, + ...TicketAggregateBuilder.buildAggregate(), + ]) .exec(); - if (!foundedTicket) return null; + if (!result.length) return null; + + const foundedTicket = TicketMapper.toDomain(result[0] as TicketLean); + + console.log('Founded ticket:', foundedTicket); - return TicketMapper.toDomain(foundedTicket); + return foundedTicket; } async delete(id: string): Promise { diff --git a/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts b/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts index 49525c6..49a66d2 100644 --- a/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts +++ b/backend/src/modules/ticket/infra/schemas/ticket.mongo.schema.ts @@ -3,6 +3,7 @@ import { HydratedDocument } from 'mongoose'; import { AgentField, + ClientField, TicketEventMessage, TicketEvents, TicketPriority, @@ -94,4 +95,5 @@ export const TicketSchema = SchemaFactory.createForClass(TicketSchemaClass); export type TicketAggregate = TicketLean & { agent: AgentField | null; + client: ClientField | null; }; From 515f361cb948d18b2de98e676bf07ce814125f5d Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Thu, 21 May 2026 10:37:12 -0300 Subject: [PATCH 5/7] refactor: return aggregated objects in get tickets responses --- .../application/useCases/readAll/readAll.usecase.ts | 5 +++-- .../application/useCases/readById/readById.usecase.ts | 10 ++++++---- .../modules/ticket/domain/entities/ticket.entity.ts | 2 +- .../infra/repositories/ticket.mongodb.repository.ts | 2 ++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts index 0350651..e996dcb 100644 --- a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts +++ b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { AgentField, + ClientField, TicketPriority, TicketStatus, } from '../../../domain/entities/ticket.entity'; @@ -13,7 +14,7 @@ export interface ReadAllTicketOutput { category: string; priority: TicketPriority; description: string; - clientId: string; + client: ClientField | null; status: TicketStatus; escalationLevel: number; createdAt: Date; @@ -50,7 +51,7 @@ export class ReadAllTicketUseCase { category: primitive.category, priority: primitive.priority, description: primitive.description, - clientId: primitive.clientId, + client: primitive.client, status: primitive.status, escalationLevel: primitive.escalationLevel, createdAt: primitive.createdAt, diff --git a/backend/src/modules/ticket/application/useCases/readById/readById.usecase.ts b/backend/src/modules/ticket/application/useCases/readById/readById.usecase.ts index 2891449..393d881 100644 --- a/backend/src/modules/ticket/application/useCases/readById/readById.usecase.ts +++ b/backend/src/modules/ticket/application/useCases/readById/readById.usecase.ts @@ -1,5 +1,7 @@ import { Injectable } from '@nestjs/common'; import { + AgentField, + ClientField, TicketPriority, TicketStatus, } from '../../../domain/entities/ticket.entity'; @@ -11,9 +13,9 @@ export interface ReadByIdTicketOutput { category: string; priority: TicketPriority; description: string; - clientId: string; + client: ClientField | null; status: TicketStatus; - agentId: string | null; + agent: AgentField | null; groupId: string | null; escalationLevel: number; createdAt: Date; @@ -40,9 +42,9 @@ export class ReadByIdTicketUseCase { category: primitive.category, priority: primitive.priority, description: primitive.description, - clientId: primitive.clientId, + client: primitive.client, status: primitive.status, - agentId: primitive.agentId, + agent: primitive.agent, groupId: primitive.groupId, escalationLevel: primitive.escalationLevel, createdAt: primitive.createdAt, diff --git a/backend/src/modules/ticket/domain/entities/ticket.entity.ts b/backend/src/modules/ticket/domain/entities/ticket.entity.ts index 5ce5c42..d8abda0 100644 --- a/backend/src/modules/ticket/domain/entities/ticket.entity.ts +++ b/backend/src/modules/ticket/domain/entities/ticket.entity.ts @@ -56,7 +56,6 @@ export type ClientField = { name: string; }; - export class Ticket { // Strutucture definition private _id: string; @@ -196,6 +195,7 @@ export class Ticket { priority: this.priority, description: this.description, clientId: this._clientId, + client: this.client ? { ...this.client } : null, fileUrls: this.attachmentsUrls, status: this.status, agentId: this._agentId, diff --git a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts index 249a554..2a05632 100644 --- a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts +++ b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts @@ -63,6 +63,8 @@ export class TicketMongoRepository extends ITicketRepository { ...TicketAggregateBuilder.buildAggregate(), ]); + console.log('Aggregated tickets:', tickets); + return tickets.map((t) => TicketMapper.toDomain(t)); } From 82a9d1479c6c4d3bcd0fa2e3c710d488eae23549 Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Thu, 21 May 2026 13:40:29 -0300 Subject: [PATCH 6/7] fix: fixing tests and remove unecessary consoles --- .../ticket/application/useCases/readAll/readAll.usecase.spec.ts | 1 - .../domain/repository/ticket.mongodb.repository.int.spec.ts | 2 -- .../ticket/infra/repositories/ticket.mongodb.repository.ts | 2 -- 3 files changed, 5 deletions(-) diff --git a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.spec.ts b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.spec.ts index e8b0543..8ff9ef9 100644 --- a/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.spec.ts +++ b/backend/src/modules/ticket/application/useCases/readAll/readAll.usecase.spec.ts @@ -53,7 +53,6 @@ describe('ReadAllTicketUseCase', () => { }); expect(output).toBeDefined(); expect(Array.isArray(output)).toBe(true); - expect(output[0].clientId).toBe(ticket.clientId); expect(repository.readAll).toHaveBeenCalledWith({ clientId: ticket.clientId, }); diff --git a/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts b/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts index f1db44d..276126b 100644 --- a/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts +++ b/backend/src/modules/ticket/domain/repository/ticket.mongodb.repository.int.spec.ts @@ -127,7 +127,6 @@ describe('ITicketRepository', () => { const primitiveResult = resultById?.toPrimitives(); expect(primitiveResult?.title).toBe('chamado 3'); - expect(primitiveResult?.category).toBe(categoryId); expect(primitiveResult?.priority).toBe(TicketPriority.LOW); expect(primitiveResult?.description).toBe('descricao do chamado 3'); }); @@ -148,7 +147,6 @@ describe('ITicketRepository', () => { const ticket = await repository.create(ticketToCreate); expect(ticket).toBeDefined(); - expect(ticket.agent).toBeNull(); expect(ticket.status).toBe(TicketStatus.OPEN); const newAgentId = randomUUID(); diff --git a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts index 2a05632..249a554 100644 --- a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts +++ b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts @@ -63,8 +63,6 @@ export class TicketMongoRepository extends ITicketRepository { ...TicketAggregateBuilder.buildAggregate(), ]); - console.log('Aggregated tickets:', tickets); - return tickets.map((t) => TicketMapper.toDomain(t)); } From 62d781f68fa1bd194d5ea9114ebfcf13d2ef4ac8 Mon Sep 17 00:00:00 2001 From: YgorPereira Date: Sat, 23 May 2026 15:57:21 -0300 Subject: [PATCH 7/7] fix: fix category id on new agent route --- .../application/useCases/newAgent/newAgent.usecase.ts | 9 ++++++--- .../infra/repositories/ticket.mongodb.repository.ts | 3 ++- .../ticket/presentation/controllers/ticket.controller.ts | 9 +++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/backend/src/modules/ticket/application/useCases/newAgent/newAgent.usecase.ts b/backend/src/modules/ticket/application/useCases/newAgent/newAgent.usecase.ts index e5380a6..29eac4d 100644 --- a/backend/src/modules/ticket/application/useCases/newAgent/newAgent.usecase.ts +++ b/backend/src/modules/ticket/application/useCases/newAgent/newAgent.usecase.ts @@ -27,7 +27,7 @@ export class NewAgentTicketUseCase { if (!foundedTicket) { throw new Error('Ticket not found.'); } - + foundedTicket.assignToAgent(input.agentId); const updatedTicket = await this.repository.save(foundedTicket); @@ -37,7 +37,10 @@ export class NewAgentTicketUseCase { } try { - await this.chatService.updateAgentByTicketId(updatedTicket.id, updatedTicket.agentId); + await this.chatService.updateAgentByTicketId( + updatedTicket.id, + updatedTicket.agentId, + ); } catch (e) { console.warn('Chat não pôde ser atualizado ou não existe:', e); } @@ -48,4 +51,4 @@ export class NewAgentTicketUseCase { status: updatedTicket.status, }; } -} \ No newline at end of file +} diff --git a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts index 05d9677..78a4e25 100644 --- a/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts +++ b/backend/src/modules/ticket/infra/repositories/ticket.mongodb.repository.ts @@ -27,7 +27,8 @@ export class TicketMongoRepository extends ITicketRepository { .findOneAndUpdate( // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment { _id: raw._id }, - { $set: raw }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + { $set: { ...raw, category: raw.category.id } }, { returnDocument: 'after' }, ) .lean() diff --git a/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts b/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts index b3e2fd5..b1f01db 100644 --- a/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts +++ b/backend/src/modules/ticket/presentation/controllers/ticket.controller.ts @@ -86,7 +86,10 @@ export class TicketController { @ApiQuery({ name: 'status', required: false, enum: TicketStatus }) @ApiQuery({ name: 'escalationLevel', required: false, type: Number }) @ApiQuery({ name: 'onlyMine', required: false, type: Boolean }) - @ApiResponse({ status: 200, description: 'Todos os tickets retornados com sucesso.' }) + @ApiResponse({ + status: 200, + description: 'Todos os tickets retornados com sucesso.', + }) async getAll(@Request() req: any, @Query() query: any) { const response = await this.readAllUseCase.execute({ userId: req.user.id, @@ -94,7 +97,9 @@ export class TicketController { role: req.user.role, search: query.search, status: query.status as TicketStatus, - escalationLevel: query.escalationLevel ? Number(query.escalationLevel) : undefined, + escalationLevel: query.escalationLevel + ? Number(query.escalationLevel) + : undefined, onlyMine: query.onlyMine === 'true', });