From 690aacfb6eeefbfe3792229659663f6d19ecef75 Mon Sep 17 00:00:00 2001 From: "Aditya @ArchLinux" <132184385+adityadeshlahre@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:10:15 +0530 Subject: [PATCH 1/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Types=20dixa=20added?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ticketing/contact/services/dixa/types.ts | 10 ++ .../ticketing/ticket/services/dixa/index.ts | 115 ++++++++++++++++ .../ticketing/ticket/services/dixa/mapper.ts | 128 ++++++++++++++++++ .../ticketing/ticket/services/dixa/types.ts | 68 ++++++++++ .../src/ticketing/user/services/dixa/types.ts | 35 +++++ 5 files changed, 356 insertions(+) create mode 100644 packages/api/src/ticketing/contact/services/dixa/types.ts create mode 100644 packages/api/src/ticketing/ticket/services/dixa/index.ts create mode 100644 packages/api/src/ticketing/ticket/services/dixa/mapper.ts create mode 100644 packages/api/src/ticketing/ticket/services/dixa/types.ts create mode 100644 packages/api/src/ticketing/user/services/dixa/types.ts diff --git a/packages/api/src/ticketing/contact/services/dixa/types.ts b/packages/api/src/ticketing/contact/services/dixa/types.ts new file mode 100644 index 000000000..fafb94084 --- /dev/null +++ b/packages/api/src/ticketing/contact/services/dixa/types.ts @@ -0,0 +1,10 @@ +export interface dixaContactOutput { + address: string; + senderOverride: string; + name: string; + _type: string; +} + +export interface dixaContactInput { + contact_endpoint_id: string; +} diff --git a/packages/api/src/ticketing/ticket/services/dixa/index.ts b/packages/api/src/ticketing/ticket/services/dixa/index.ts new file mode 100644 index 000000000..d348accd3 --- /dev/null +++ b/packages/api/src/ticketing/ticket/services/dixa/index.ts @@ -0,0 +1,115 @@ +import { Injectable } from '@nestjs/common'; +import { LoggerService } from '@@core/logger/logger.service'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { TicketingObject } from '@ticketing/@lib/@types'; +import { ITicketService } from '@ticketing/ticket/types'; +import { ApiResponse } from '@@core/utils/types'; +import axios from 'axios'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; +import { ServiceRegistry } from '../registry.service'; +import { dixaTicketInput, dixaTicketOutput } from './types'; +import { Utils } from '@ticketing/@lib/@utils'; + +@Injectable() +export class dixa implements ITicketService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + private utils: Utils, + ) { + this.logger.setContext( + TicketingObject.ticket.toUpperCase() + ':' + dixa.name, + ); + this.registry.registerService('dixa', this); + } + + async addTicket( + ticketData: dixaTicketInput, + linkedUserId: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'dixa', + vertical: 'ticketing', + }, + }); + + //Add comment by calling the unified comment function but first insert the ticket base data + + const resp = await axios.post( + `${connection.account_url}/conversations`, + JSON.stringify(ticketData), + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + + // Add comment if someone wants to add one when creation of the ticket + + return { + data: resp.data, + message: 'dixa ticket created', + statusCode: 201, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'dixa', + TicketingObject.ticket, + ActionType.POST, + ); + } + } + + async syncTickets( + linkedUserId: string, + remote_ticket_id?: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'dixa', + vertical: 'ticketing', + }, + }); + const resp = await axios.get( + `${connection.account_url}/conversations/${remote_ticket_id}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced dixa tickets !`); + + return { + data: resp.data, + message: 'dixa tickets retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'dixa', + TicketingObject.ticket, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/ticketing/ticket/services/dixa/mapper.ts b/packages/api/src/ticketing/ticket/services/dixa/mapper.ts new file mode 100644 index 000000000..d117fe330 --- /dev/null +++ b/packages/api/src/ticketing/ticket/services/dixa/mapper.ts @@ -0,0 +1,128 @@ +import { ITicketMapper } from '@ticketing/ticket/types'; +import { dixaTicketInput, dixaTicketOutput } from './types'; +import { + UnifiedTicketInput, + UnifiedTicketOutput, +} from '@ticketing/ticket/types/model.unified'; +import { Utils } from '@ticketing/@lib/@utils'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class dixaTicketMapper implements ITicketMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('ticketing', 'ticket', 'dixa', this); + } + async desunify( + source: UnifiedTicketInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const body_: any = {}; + + if (source.comment.creator_type === 'user') { + body_.author_id = await this.utils.getAsigneeRemoteIdFromUserUuid( + source.comment.user_id, + ); + } + if (source.comment.attachments) { + body_.attachments = source.comment.attachments; + } + const result: dixaTicketInput = { + _type: 'discussion', + subject: source.name, + message: { + body: source.comment.body, + ...body_, + }, + }; + + // if (source.assigned_to && source.assigned_to.length > 0) { + // const res: string[] = []; + // for (const assignee of source.assigned_to) { + // const data = await this.utils.getAsigneeRemoteIdFromUserUuid(assignee); + // if (data) { + // res.push(data); + // } + // } + // result.teammate_ids = res; + // } + + // if (source.tags) { + // result.tags = source.tags; + // } + + if (customFieldMappings && source.field_mappings) { + for (const [k, v] of Object.entries(source.field_mappings)) { + const mapping = customFieldMappings.find( + (mapping) => mapping.slug === k, + ); + if (mapping) { + result[mapping.remote_id] = v; + } + } + } + + return result; + } + + async unify( + source: dixaTicketOutput | dixaTicketOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + // If the source is not an array, convert it to an array for mapping + const sourcesArray = Array.isArray(source) ? source : [source]; + + return Promise.all( + sourcesArray.map((ticket) => + this.mapSingleTicketToUnified(ticket, customFieldMappings), + ), + ); + } + + private async mapSingleTicketToUnified( + ticket: dixaTicketOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): Promise { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = ticket.id[mapping.remote_id]; + } + } + + let opts: any; + + if (ticket.assignment.agentId) { + //fetch the right assignee uuid from remote id + const user_id = await this.utils.getUserUuidFromRemoteId( + String(ticket.assignment.agentId), + 'dixa', + ); + if (user_id) { + opts = { assigned_to: [user_id] }; + } + } + + const unifiedTicket: UnifiedTicketOutput = { + remote_id: ticket.id, + name: ticket.customAttributes[0].name, + status: ticket.state, + description: ticket.customAttributes[0].value, // todo: ? + due_date: new Date(ticket.stateUpdatedAt), // todo ? + // tags: ticket.tags?.map((tag) => tag.name), + field_mappings: field_mappings, + ...opts, + }; + + return unifiedTicket; + } +} diff --git a/packages/api/src/ticketing/ticket/services/dixa/types.ts b/packages/api/src/ticketing/ticket/services/dixa/types.ts new file mode 100644 index 000000000..643925caa --- /dev/null +++ b/packages/api/src/ticketing/ticket/services/dixa/types.ts @@ -0,0 +1,68 @@ +export type dixaTicketInput = Partial; +export type dixaTicketOutput = Conversation; + +interface CreateConversationPayload { + requesterId: string; + emailIntegrationId: string; + subject: string; + message: Message; + language: string; + _type: string; +} + +interface Message { + content: Content; + attachments: any[]; + _type: string; +} + +interface Content { + value: string; + _type: string; +} + +interface Conversation { + id: number; + requesterId: string; + channel: string; + createdAt: string; + direction: string; + state: string; + stateUpdatedAt: string; + assignment: Assignment; + queue: Queue; + browserInfo: BrowserInfo; + language: string; + link: Link; + customAttributes: CustomAttribute[]; + _type: string; +} + +interface Assignment { + agentId: string; + assignedAt: string; +} + +interface Queue { + id: string; + queuedAt: string; +} + +interface BrowserInfo { + name: string; + version: string; + ipAddress: string; + originatingUrl: string; +} + +interface Link { + parentId: number; + _type: string; +} + +interface CustomAttribute { + id: string; + name: string; + identifier: string; + value: string; +} diff --git a/packages/api/src/ticketing/user/services/dixa/types.ts b/packages/api/src/ticketing/user/services/dixa/types.ts new file mode 100644 index 000000000..62eb07890 --- /dev/null +++ b/packages/api/src/ticketing/user/services/dixa/types.ts @@ -0,0 +1,35 @@ +export interface dixaUserInput { + displayName: string; + email: string; + phoneNumber: string; + additionalEmails: string[]; + additionalPhoneNumbers: string[]; + firstName: string; + lastName: string; + middleNames: any[]; + avatarUrl: string; + externalId: string; +} + +export interface dixaUserOutput { + id: string; + createdAt: string; + displayName: string; + email: string; + phoneNumber: string; + additionalEmails: string[]; + additionalPhoneNumbers: string[]; + firstName: string; + lastName: string; + middleNames: any[]; + avatarUrl: string; + externalId: string; + customAttributes: CustomAttribute[]; +} + +interface CustomAttribute { + id: string; + name: string; + identifier: string; + value: string; +} From d08dc104c2dc6d0c7d792ca8266d3c89d17a92ea Mon Sep 17 00:00:00 2001 From: "Aditya @ArchLinux" <132184385+adityadeshlahre@users.noreply.github.com> Date: Sun, 30 Jun 2024 20:55:27 +0530 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20Added=20types=20+?= =?UTF-8?q?=20INDEX=20&=20MAPPER=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added types/index/mapper --- packages/api/scripts/init.sql | 5 +- packages/api/scripts/seed.sql | 8 +- .../types/original/original.ticketing.ts | 36 +++++++-- .../attachment/services/dixa/index.ts | 0 .../attachment/services/dixa/mapper.ts | 0 .../attachment/services/dixa/types.ts | 13 ++++ .../ticketing/comment/services/dixa/index.ts | 0 .../comment/services/dixa/mappers.ts | 0 .../ticketing/comment/services/dixa/types.ts | 3 + .../src/ticketing/contact/contact.module.ts | 2 + .../ticketing/contact/services/dixa/index.ts | 70 +++++++++++++++++ .../contact/services/dixa/mappers.ts | 71 ++++++++++++++++++ .../ticketing/contact/services/dixa/types.ts | 4 +- .../ticketing/ticket/services/dixa/index.ts | 28 +++---- .../ticketing/ticket/services/dixa/mapper.ts | 16 ++-- .../ticketing/ticket/services/dixa/types.ts | 4 +- .../api/src/ticketing/ticket/ticket.module.ts | 2 + .../src/ticketing/user/services/dixa/index.ts | 68 +++++++++++++++++ .../ticketing/user/services/dixa/mappers.ts | 75 +++++++++++++++++++ .../src/ticketing/user/services/dixa/types.ts | 4 +- .../api/src/ticketing/user/user.module.ts | 2 + packages/shared/src/connectors/enum.ts | 3 +- packages/shared/src/connectors/index.ts | 2 +- 23 files changed, 375 insertions(+), 41 deletions(-) create mode 100644 packages/api/src/ticketing/attachment/services/dixa/index.ts create mode 100644 packages/api/src/ticketing/attachment/services/dixa/mapper.ts create mode 100644 packages/api/src/ticketing/attachment/services/dixa/types.ts create mode 100644 packages/api/src/ticketing/comment/services/dixa/index.ts create mode 100644 packages/api/src/ticketing/comment/services/dixa/mappers.ts create mode 100644 packages/api/src/ticketing/comment/services/dixa/types.ts create mode 100644 packages/api/src/ticketing/contact/services/dixa/index.ts create mode 100644 packages/api/src/ticketing/contact/services/dixa/mappers.ts create mode 100644 packages/api/src/ticketing/user/services/dixa/index.ts create mode 100644 packages/api/src/ticketing/user/services/dixa/mappers.ts diff --git a/packages/api/scripts/init.sql b/packages/api/scripts/init.sql index c5f0e5c67..b459fc67e 100644 --- a/packages/api/scripts/init.sql +++ b/packages/api/scripts/init.sql @@ -479,7 +479,10 @@ CREATE TABLE connector_sets tcg_front boolean NOT NULL, crm_zendesk boolean NOT NULL, crm_close boolean NOT NULL, - CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) + tcg_dixa boolean NOT NULL, + tcg_github boolean NOT NULL, + tcg_hubspot boolean NOT NULL, +CONSTRAINT PK_project_connector PRIMARY KEY ( id_connector_set ) ); diff --git a/packages/api/scripts/seed.sql b/packages/api/scripts/seed.sql index 620985342..e08644c34 100644 --- a/packages/api/scripts/seed.sql +++ b/packages/api/scripts/seed.sql @@ -1,10 +1,10 @@ INSERT INTO users (id_user, identification_strategy, email, password_hash, first_name, last_name) VALUES ('0ce39030-2901-4c56-8db0-5e326182ec6b', 'b2c','local@panora.dev', '$2b$10$Y7Q8TWGyGuc5ecdIASbBsuXMo3q/Rs3/cnY.mLZP4tUgfGUOCUBlG', 'local', 'Panora'); -INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab) VALUES - ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), - ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); +INSERT INTO connector_sets (id_connector_set, crm_hubspot, crm_zoho, crm_pipedrive, crm_attio, crm_zendesk, crm_close, tcg_zendesk, tcg_gorgias, tcg_front, tcg_jira, tcg_gitlab, tcg_dixa, tcg_github, tcg_hubspot) VALUES + ('1709da40-17f7-4d3a-93a0-96dc5da6ddd7', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('852dfff8-ab63-4530-ae49-e4b2924407f8', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE), + ('aed0f856-f802-4a79-8640-66d441581a99', TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE); INSERT INTO projects (id_project, name, sync_mode, id_user, id_connector_set) VALUES ('1e468c15-aa57-4448-aa2b-7fed640d1e3d', 'Project 1', 'pool', '0ce39030-2901-4c56-8db0-5e326182ec6b', '1709da40-17f7-4d3a-93a0-96dc5da6ddd7'), diff --git a/packages/api/src/@core/utils/types/original/original.ticketing.ts b/packages/api/src/@core/utils/types/original/original.ticketing.ts index 8410fe32f..cae23903b 100644 --- a/packages/api/src/@core/utils/types/original/original.ticketing.ts +++ b/packages/api/src/@core/utils/types/original/original.ticketing.ts @@ -1,3 +1,19 @@ +import { + DixaContactInput, + DixaContactOutput, +} from '@ticketing/contact/services/dixa/types'; +import { + DixaTicketInput, + DixaTicketOutput, +} from '@ticketing/ticket/services/dixa/types'; +import { + DixaUserInput, + DixaUserOutput, +} from '@ticketing/user/services/dixa/types'; +import { + GitlabUserInput, + GitlabUserOutput, +} from '@ticketing/user/services/gitlab/types'; import { FrontAccountInput, FrontAccountOutput, @@ -137,7 +153,8 @@ export type OriginalTicketInput = | HubspotTicketInput | GorgiasTicketInput | JiraTicketInput - | GitlabTicketInput; + | GitlabTicketInput + | DixaTicketInput; //| JiraServiceMgmtTicketInput; /* comment */ @@ -153,7 +170,9 @@ export type OriginalUserInput = | ZendeskUserInput | FrontUserInput | GorgiasUserInput - | JiraUserInput; + | JiraUserInput + | DixaUserInput + | GitlabUserInput; //| JiraServiceMgmtUserInput; /* account */ export type OriginalAccountInput = ZendeskAccountInput | FrontAccountInput; @@ -161,7 +180,8 @@ export type OriginalAccountInput = ZendeskAccountInput | FrontAccountInput; export type OriginalContactInput = | ZendeskContactInput | FrontContactInput - | GorgiasContactInput; + | GorgiasContactInput + | DixaContactInput; /* tag */ export type OriginalTagInput = @@ -204,7 +224,8 @@ export type OriginalTicketOutput = | HubspotTicketOutput | GorgiasTicketOutput | JiraTicketOutput - | GitlabTicketOutput; + | GitlabTicketOutput + | DixaTicketOutput; /* comment */ export type OriginalCommentOutput = @@ -218,14 +239,17 @@ export type OriginalUserOutput = | ZendeskUserOutput | FrontUserOutput | GorgiasUserOutput - | JiraUserOutput; + | JiraUserOutput + | DixaUserOutput + | GitlabUserOutput; /* account */ export type OriginalAccountOutput = ZendeskAccountOutput | FrontAccountOutput; /* contact */ export type OriginalContactOutput = | ZendeskContactOutput | FrontContactOutput - | GorgiasContactOutput; + | GorgiasContactOutput + | DixaContactOutput; /* tag */ export type OriginalTagOutput = diff --git a/packages/api/src/ticketing/attachment/services/dixa/index.ts b/packages/api/src/ticketing/attachment/services/dixa/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ticketing/attachment/services/dixa/mapper.ts b/packages/api/src/ticketing/attachment/services/dixa/mapper.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ticketing/attachment/services/dixa/types.ts b/packages/api/src/ticketing/attachment/services/dixa/types.ts new file mode 100644 index 000000000..a3a80a758 --- /dev/null +++ b/packages/api/src/ticketing/attachment/services/dixa/types.ts @@ -0,0 +1,13 @@ +export type DixaAttachmentOutput = { + id: string; + filename: string; + url: string; + content_type: string; + size: number; + metadata: AttachmentMetadata; +}; + +type AttachmentMetadata = { + url: string; + pretty_name: string; +}; diff --git a/packages/api/src/ticketing/comment/services/dixa/index.ts b/packages/api/src/ticketing/comment/services/dixa/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ticketing/comment/services/dixa/mappers.ts b/packages/api/src/ticketing/comment/services/dixa/mappers.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/api/src/ticketing/comment/services/dixa/types.ts b/packages/api/src/ticketing/comment/services/dixa/types.ts new file mode 100644 index 000000000..a24870fd2 --- /dev/null +++ b/packages/api/src/ticketing/comment/services/dixa/types.ts @@ -0,0 +1,3 @@ +export type DixaCommentInput = {}; + +export type DixaCommentOutput = {}; diff --git a/packages/api/src/ticketing/contact/contact.module.ts b/packages/api/src/ticketing/contact/contact.module.ts index 52270209d..2118e66bf 100644 --- a/packages/api/src/ticketing/contact/contact.module.ts +++ b/packages/api/src/ticketing/contact/contact.module.ts @@ -1,3 +1,4 @@ +import { DixaService } from './services/dixa'; import { Module } from '@nestjs/common'; import { SyncService } from './sync/sync.service'; import { WebhookService } from '@@core/webhook/webhook.service'; @@ -46,6 +47,7 @@ import { ConnectionUtils } from '@@core/connections/@utils'; ZendeskService, FrontService, GorgiasService, + DixaService, ], exports: [ SyncService, diff --git a/packages/api/src/ticketing/contact/services/dixa/index.ts b/packages/api/src/ticketing/contact/services/dixa/index.ts new file mode 100644 index 000000000..59ed68ae3 --- /dev/null +++ b/packages/api/src/ticketing/contact/services/dixa/index.ts @@ -0,0 +1,70 @@ +import { Injectable } from '@nestjs/common'; +import { LoggerService } from '@@core/logger/logger.service'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { TicketingObject } from '@ticketing/@lib/@types'; +import { ApiResponse } from '@@core/utils/types'; +import axios from 'axios'; +import { ServiceRegistry } from '../registry.service'; +import { IContactService } from '@ticketing/contact/types'; +import { DixaContactOutput } from './types'; + +@Injectable() +export class DixaService implements IContactService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.contact.toUpperCase() + ':' + DixaService.name, + ); + this.registry.registerService('Dixa', this); + } + + async syncContacts( + linkedUserId: string, + remote_account_id: string, + ): Promise> { + try { + if (!remote_account_id) + throw new ReferenceError('remote account id not found'); + + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'Dixa', + vertical: 'ticketing', + }, + }); + + const resp = await axios.get( + `${connection.account_url}/beta/contact-endpoints/${remote_account_id}`, + { + headers: { + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced Dixa contacts !`); + + return { + data: resp.data, + message: 'Dixa contacts retrieved', + statusCode: 200, + }; + } catch (error) { + throw error; + /*handle3rdPartyServiceError( + error, + this.logger, + 'Dixa', + TicketingObject.contact, + ActionType.GET, + );*/ + } + } +} diff --git a/packages/api/src/ticketing/contact/services/dixa/mappers.ts b/packages/api/src/ticketing/contact/services/dixa/mappers.ts new file mode 100644 index 000000000..a8502daa8 --- /dev/null +++ b/packages/api/src/ticketing/contact/services/dixa/mappers.ts @@ -0,0 +1,71 @@ +import { IContactMapper } from '@ticketing/contact/types'; +import { DixaContactInput, DixaContactOutput } from './types'; +import { + UnifiedContactInput, + UnifiedContactOutput, +} from '@ticketing/contact/types/model.unified'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { Injectable } from '@nestjs/common'; +import { Utils } from '@ticketing/@lib/@utils'; + +@Injectable() +export class DixaContactMapper implements IContactMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('ticketing', 'contact', 'Dixa', this); + } + desunify( + source: UnifiedContactInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): DixaContactInput { + return; + } + + unify( + source: DixaContactOutput | DixaContactOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedContactOutput | UnifiedContactOutput[] { + // If the source is not an array, convert it to an array for mapping + const sourcesArray = Array.isArray(source) ? source : [source]; + + return sourcesArray.map((contact) => + this.mapSingleContactToUnified(contact, customFieldMappings), + ); + } + + private mapSingleContactToUnified( + contact: DixaContactOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedContactOutput { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = contact._type[mapping.remote_id]; + } + } + // const emailHandle = contact.handles.find( + // (handle) => handle.source === 'email', + // ); + // const phoneHandle = contact.handles.find( + // (handle) => handle.source === 'phone', + // ); + + const unifiedContact: UnifiedContactOutput = { + remote_id: contact._type, + name: contact.name, + details: contact.address, + email_address: contact.senderOverride, + field_mappings: field_mappings, + }; + + return unifiedContact; + } +} diff --git a/packages/api/src/ticketing/contact/services/dixa/types.ts b/packages/api/src/ticketing/contact/services/dixa/types.ts index fafb94084..c1874afda 100644 --- a/packages/api/src/ticketing/contact/services/dixa/types.ts +++ b/packages/api/src/ticketing/contact/services/dixa/types.ts @@ -1,10 +1,10 @@ -export interface dixaContactOutput { +export interface DixaContactOutput { address: string; senderOverride: string; name: string; _type: string; } -export interface dixaContactInput { +export interface DixaContactInput { contact_endpoint_id: string; } diff --git a/packages/api/src/ticketing/ticket/services/dixa/index.ts b/packages/api/src/ticketing/ticket/services/dixa/index.ts index d348accd3..3c00ab2bd 100644 --- a/packages/api/src/ticketing/ticket/services/dixa/index.ts +++ b/packages/api/src/ticketing/ticket/services/dixa/index.ts @@ -8,11 +8,11 @@ import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; import { ServiceRegistry } from '../registry.service'; -import { dixaTicketInput, dixaTicketOutput } from './types'; +import { DixaTicketInput, DixaTicketOutput } from './types'; import { Utils } from '@ticketing/@lib/@utils'; @Injectable() -export class dixa implements ITicketService { +export class DixaService implements ITicketService { constructor( private prisma: PrismaService, private logger: LoggerService, @@ -21,20 +21,20 @@ export class dixa implements ITicketService { private utils: Utils, ) { this.logger.setContext( - TicketingObject.ticket.toUpperCase() + ':' + dixa.name, + TicketingObject.ticket.toUpperCase() + ':' + DixaService.name, ); - this.registry.registerService('dixa', this); + this.registry.registerService('Dixa', this); } async addTicket( - ticketData: dixaTicketInput, + ticketData: DixaTicketInput, linkedUserId: string, - ): Promise> { + ): Promise> { try { const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, - provider_slug: 'dixa', + provider_slug: 'Dixa', vertical: 'ticketing', }, }); @@ -58,14 +58,14 @@ export class dixa implements ITicketService { return { data: resp.data, - message: 'dixa ticket created', + message: 'Dixa ticket created', statusCode: 201, }; } catch (error) { handle3rdPartyServiceError( error, this.logger, - 'dixa', + 'Dixa', TicketingObject.ticket, ActionType.POST, ); @@ -75,12 +75,12 @@ export class dixa implements ITicketService { async syncTickets( linkedUserId: string, remote_ticket_id?: string, - ): Promise> { + ): Promise> { try { const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, - provider_slug: 'dixa', + provider_slug: 'Dixa', vertical: 'ticketing', }, }); @@ -95,18 +95,18 @@ export class dixa implements ITicketService { }, }, ); - this.logger.log(`Synced dixa tickets !`); + this.logger.log(`Synced Dixa tickets !`); return { data: resp.data, - message: 'dixa tickets retrieved', + message: 'Dixa tickets retrieved', statusCode: 200, }; } catch (error) { handle3rdPartyServiceError( error, this.logger, - 'dixa', + 'Dixa', TicketingObject.ticket, ActionType.GET, ); diff --git a/packages/api/src/ticketing/ticket/services/dixa/mapper.ts b/packages/api/src/ticketing/ticket/services/dixa/mapper.ts index d117fe330..fbf9a488a 100644 --- a/packages/api/src/ticketing/ticket/services/dixa/mapper.ts +++ b/packages/api/src/ticketing/ticket/services/dixa/mapper.ts @@ -1,5 +1,5 @@ import { ITicketMapper } from '@ticketing/ticket/types'; -import { dixaTicketInput, dixaTicketOutput } from './types'; +import { DixaTicketInput, DixaTicketOutput } from './types'; import { UnifiedTicketInput, UnifiedTicketOutput, @@ -9,9 +9,9 @@ import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; import { Injectable } from '@nestjs/common'; @Injectable() -export class dixaTicketMapper implements ITicketMapper { +export class DixaTicketMapper implements ITicketMapper { constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { - this.mappersRegistry.registerService('ticketing', 'ticket', 'dixa', this); + this.mappersRegistry.registerService('ticketing', 'ticket', 'Dixa', this); } async desunify( source: UnifiedTicketInput, @@ -19,7 +19,7 @@ export class dixaTicketMapper implements ITicketMapper { slug: string; remote_id: string; }[], - ): Promise { + ): Promise { const body_: any = {}; if (source.comment.creator_type === 'user') { @@ -30,7 +30,7 @@ export class dixaTicketMapper implements ITicketMapper { if (source.comment.attachments) { body_.attachments = source.comment.attachments; } - const result: dixaTicketInput = { + const result: DixaTicketInput = { _type: 'discussion', subject: source.name, message: { @@ -69,7 +69,7 @@ export class dixaTicketMapper implements ITicketMapper { } async unify( - source: dixaTicketOutput | dixaTicketOutput[], + source: DixaTicketOutput | DixaTicketOutput[], customFieldMappings?: { slug: string; remote_id: string; @@ -86,7 +86,7 @@ export class dixaTicketMapper implements ITicketMapper { } private async mapSingleTicketToUnified( - ticket: dixaTicketOutput, + ticket: DixaTicketOutput, customFieldMappings?: { slug: string; remote_id: string; @@ -105,7 +105,7 @@ export class dixaTicketMapper implements ITicketMapper { //fetch the right assignee uuid from remote id const user_id = await this.utils.getUserUuidFromRemoteId( String(ticket.assignment.agentId), - 'dixa', + 'Dixa', ); if (user_id) { opts = { assigned_to: [user_id] }; diff --git a/packages/api/src/ticketing/ticket/services/dixa/types.ts b/packages/api/src/ticketing/ticket/services/dixa/types.ts index 643925caa..8f4b5ae64 100644 --- a/packages/api/src/ticketing/ticket/services/dixa/types.ts +++ b/packages/api/src/ticketing/ticket/services/dixa/types.ts @@ -1,5 +1,5 @@ -export type dixaTicketInput = Partial; -export type dixaTicketOutput = Conversation; +export type DixaTicketInput = Partial; +export type DixaTicketOutput = Conversation; interface CreateConversationPayload { requesterId: string; diff --git a/packages/api/src/ticketing/ticket/ticket.module.ts b/packages/api/src/ticketing/ticket/ticket.module.ts index 026d0a576..d65b3b450 100644 --- a/packages/api/src/ticketing/ticket/ticket.module.ts +++ b/packages/api/src/ticketing/ticket/ticket.module.ts @@ -1,3 +1,4 @@ +import { DixaService } from './services/dixa'; import { GitlabService } from './services/gitlab'; import { Module } from '@nestjs/common'; import { TicketController } from './ticket.controller'; @@ -53,6 +54,7 @@ import { ConnectionUtils } from '@@core/connections/@utils'; JiraService, GorgiasService, GitlabService, + DixaService, ], exports: [ SyncService, diff --git a/packages/api/src/ticketing/user/services/dixa/index.ts b/packages/api/src/ticketing/user/services/dixa/index.ts new file mode 100644 index 000000000..87e05a242 --- /dev/null +++ b/packages/api/src/ticketing/user/services/dixa/index.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { LoggerService } from '@@core/logger/logger.service'; +import { PrismaService } from '@@core/prisma/prisma.service'; +import { EncryptionService } from '@@core/encryption/encryption.service'; +import { TicketingObject } from '@ticketing/@lib/@types'; +import { ApiResponse } from '@@core/utils/types'; +import axios from 'axios'; +import { ActionType, handle3rdPartyServiceError } from '@@core/utils/errors'; +import { ServiceRegistry } from '../registry.service'; +import { IUserService } from '@ticketing/user/types'; +import { DixaUserOutput } from './types'; + +@Injectable() +export class DixaService implements IUserService { + constructor( + private prisma: PrismaService, + private logger: LoggerService, + private cryptoService: EncryptionService, + private registry: ServiceRegistry, + ) { + this.logger.setContext( + TicketingObject.user.toUpperCase() + ':' + DixaService.name, + ); + this.registry.registerService('dixa', this); + } + + async syncUsers( + linkedUserId: string, + remote_user_id?: string, + ): Promise> { + try { + const connection = await this.prisma.connections.findFirst({ + where: { + id_linked_user: linkedUserId, + provider_slug: 'dixa', + vertical: 'ticketing', + }, + }); + + const resp = await axios.get( + `${connection.account_url}/endusers/${linkedUserId}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.cryptoService.decrypt( + connection.access_token, + )}`, + }, + }, + ); + this.logger.log(`Synced dixa users !`); + + return { + data: resp.data, + message: 'dixa users retrieved', + statusCode: 200, + }; + } catch (error) { + handle3rdPartyServiceError( + error, + this.logger, + 'dixa', + TicketingObject.user, + ActionType.GET, + ); + } + } +} diff --git a/packages/api/src/ticketing/user/services/dixa/mappers.ts b/packages/api/src/ticketing/user/services/dixa/mappers.ts new file mode 100644 index 000000000..d06150ded --- /dev/null +++ b/packages/api/src/ticketing/user/services/dixa/mappers.ts @@ -0,0 +1,75 @@ +import { IUserMapper } from '@ticketing/user/types'; +import { + UnifiedUserInput, + UnifiedUserOutput, +} from '@ticketing/user/types/model.unified'; +import { DixaUserInput, DixaUserOutput } from './types'; +import { MappersRegistry } from '@@core/utils/registry/mappings.registry'; +import { Injectable } from '@nestjs/common'; +import { Utils } from '@ticketing/@lib/@utils'; + +@Injectable() +export class dixaUserMapper implements IUserMapper { + constructor(private mappersRegistry: MappersRegistry, private utils: Utils) { + this.mappersRegistry.registerService('ticketing', 'user', 'dixa', this); + } + desunify( + source: UnifiedUserInput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): DixaUserInput { + return { + displayName: source.name, + email: source.email_address, + phoneNumber: '', + additionalEmails: [], + additionalPhoneNumbers: [], + firstName: source.name.split(' ')[0], + lastName: source.name.split(' ')[1] || '', + middleNames: [], + avatarUrl: '', + externalId: '', + }; + } + + unify( + source: DixaUserOutput | DixaUserOutput[], + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedUserOutput | UnifiedUserOutput[] { + // If the source is not an array, convert it to an array for mapping + const sourcesArray = Array.isArray(source) ? source : [source]; + + return sourcesArray.map((user) => + this.mapSingleUserToUnified(user, customFieldMappings), + ); + } + + private mapSingleUserToUnified( + user: DixaUserOutput, + customFieldMappings?: { + slug: string; + remote_id: string; + }[], + ): UnifiedUserOutput { + const field_mappings: { [key: string]: any } = {}; + if (customFieldMappings) { + for (const mapping of customFieldMappings) { + field_mappings[mapping.slug] = user.customAttributes[mapping.remote_id]; + } + } + + const unifiedUser: UnifiedUserOutput = { + remote_id: user.id, + name: `${user.firstName} ${user.lastName}`, + email_address: user.email, + field_mappings: field_mappings, + }; + + return unifiedUser; + } +} diff --git a/packages/api/src/ticketing/user/services/dixa/types.ts b/packages/api/src/ticketing/user/services/dixa/types.ts index 62eb07890..bf5ad3eb6 100644 --- a/packages/api/src/ticketing/user/services/dixa/types.ts +++ b/packages/api/src/ticketing/user/services/dixa/types.ts @@ -1,4 +1,4 @@ -export interface dixaUserInput { +export interface DixaUserInput { displayName: string; email: string; phoneNumber: string; @@ -11,7 +11,7 @@ export interface dixaUserInput { externalId: string; } -export interface dixaUserOutput { +export interface DixaUserOutput { id: string; createdAt: string; displayName: string; diff --git a/packages/api/src/ticketing/user/user.module.ts b/packages/api/src/ticketing/user/user.module.ts index f32e5e45f..c4e352dfc 100644 --- a/packages/api/src/ticketing/user/user.module.ts +++ b/packages/api/src/ticketing/user/user.module.ts @@ -1,3 +1,4 @@ +import { DixaService } from './services/dixa'; import { GitlabService } from './services/gitlab'; import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; @@ -49,6 +50,7 @@ import { Utils } from '@ticketing/@lib/@utils'; JiraService, GorgiasService, GitlabService, + DixaService, ], exports: [ SyncService, diff --git a/packages/shared/src/connectors/enum.ts b/packages/shared/src/connectors/enum.ts index ce0ef4aaf..891ed412f 100644 --- a/packages/shared/src/connectors/enum.ts +++ b/packages/shared/src/connectors/enum.ts @@ -12,7 +12,8 @@ export enum TicketingConnectors { FRONT = 'front', JIRA = 'jira', GORGIAS = 'gorgias', - GITLAB = 'gitlab' + GITLAB = 'gitlab', + DIXA = 'dixa' } export enum AccountingConnectors { diff --git a/packages/shared/src/connectors/index.ts b/packages/shared/src/connectors/index.ts index 00da95934..847fe3dea 100644 --- a/packages/shared/src/connectors/index.ts +++ b/packages/shared/src/connectors/index.ts @@ -2,6 +2,6 @@ export const CRM_PROVIDERS = ['zoho', 'zendesk', 'hubspot', 'pipedrive', 'attio' export const HRIS_PROVIDERS = ['']; export const ATS_PROVIDERS = ['']; export const ACCOUNTING_PROVIDERS = ['']; -export const TICKETING_PROVIDERS = ['zendesk', 'front', 'jira', 'gorgias', 'gitlab']; +export const TICKETING_PROVIDERS = ['zendesk', 'front', 'jira', 'gorgias', 'gitlab', 'dixa', 'github', 'hubspot']; export const MARKETINGAUTOMATION_PROVIDERS = ['']; export const FILESTORAGE_PROVIDERS = [''];