diff --git a/packages/api/src/crm/contact/services/hubspot/types.ts b/packages/api/src/crm/contact/services/hubspot/types.ts index 7e9de18ef..208850ca0 100644 --- a/packages/api/src/crm/contact/services/hubspot/types.ts +++ b/packages/api/src/crm/contact/services/hubspot/types.ts @@ -16,6 +16,14 @@ export interface HubspotContactInput { [key: string]: any; } +export interface HubspotContactOutput { + id: string; + properties: HubspotPropertiesOuput; + createdAt: string; + updatedAt: string; + archived: boolean; +} + type HubspotPropertiesOuput = { createdate: string; email: string; @@ -35,10 +43,3 @@ export const commonHubspotProperties = { lastname: '', // Add any other common properties here }; -export interface HubspotContactOutput { - id: string; - properties: HubspotPropertiesOuput; - createdAt: string; - updatedAt: string; - archived: boolean; -} diff --git a/packages/api/src/ticketing/comment/comment.module.ts b/packages/api/src/ticketing/comment/comment.module.ts index 0e9f8e6c7..e33d3d1ae 100644 --- a/packages/api/src/ticketing/comment/comment.module.ts +++ b/packages/api/src/ticketing/comment/comment.module.ts @@ -10,6 +10,9 @@ import { CommentController } from './comment.controller'; import { CommentService } from './services/comment.service'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from './services/registry.service'; +import { GithubService } from './services/github'; +import { FrontService } from './services/front'; +import { HubspotService } from './services/hubspot'; @Module({ imports: [ @@ -29,6 +32,9 @@ import { ServiceRegistry } from './services/registry.service'; ServiceRegistry, /* PROVIDERS SERVICES */ ZendeskService, + HubspotService, + FrontService, + GithubService, ], exports: [SyncService], }) diff --git a/packages/api/src/ticketing/comment/services/hubspot/index.ts b/packages/api/src/ticketing/comment/services/hubspot/index.ts index d443341a8..ccab00f34 100644 --- a/packages/api/src/ticketing/comment/services/hubspot/index.ts +++ b/packages/api/src/ticketing/comment/services/hubspot/index.ts @@ -32,7 +32,7 @@ export class HubspotService implements ICommentService { ): Promise> { try { //TODO: check required scope => crm.objects.contacts.write - const connection = await this.prisma.connections.findFirst({ + /*const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, provider_slug: 'hubspot_t', @@ -57,7 +57,8 @@ export class HubspotService implements ICommentService { data: resp.data, message: 'Hubspot comment created', statusCode: 201, - }; + };*/ + return; } catch (error) { handleServiceError( error, @@ -73,7 +74,7 @@ export class HubspotService implements ICommentService { id_ticket: string, ): Promise> { try { - const connection = await this.prisma.connections.findFirst({ + /*const connection = await this.prisma.connections.findFirst({ where: { id_linked_user: linkedUserId, provider_slug: 'hubspot_t', @@ -106,7 +107,8 @@ export class HubspotService implements ICommentService { data: resp.data._results, message: 'Hubspot comments retrieved', statusCode: 200, - }; + };*/ + return; } catch (error) { handleServiceError( error, diff --git a/packages/api/src/ticketing/comment/services/zendesk/types.ts b/packages/api/src/ticketing/comment/services/zendesk/types.ts index 3f248a54d..2115f377c 100644 --- a/packages/api/src/ticketing/comment/services/zendesk/types.ts +++ b/packages/api/src/ticketing/comment/services/zendesk/types.ts @@ -19,7 +19,7 @@ type BaseComment = { via?: Via; // Describes how the object was created. }; -type Attachment = { +export type Attachment = { content_type: string; // The content type of the image, e.g., "image/png". content_url: string; // A full URL where the attachment image file can be downloaded. deleted: boolean; // If true, the attachment has been deleted. @@ -40,7 +40,7 @@ type Attachment = { width: string | null; // The width of the image file in pixels, or null if unknown. }; -export type CustomField = { +export type CustomField_ = { id: string; value: any; }; diff --git a/packages/api/src/ticketing/ticket/services/front/index.ts b/packages/api/src/ticketing/ticket/services/front/index.ts index 5d013378b..b85718a78 100644 --- a/packages/api/src/ticketing/ticket/services/front/index.ts +++ b/packages/api/src/ticketing/ticket/services/front/index.ts @@ -8,7 +8,6 @@ import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; import { ActionType, handleServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; -import { OriginalTicketOutput } from '@@core/utils/types/original/original.ticketing'; import { ServiceRegistry } from '../registry.service'; import { FrontTicketInput, FrontTicketOutput } from './types'; @@ -68,7 +67,7 @@ export class FrontService implements ITicketService { async syncTickets( linkedUserId: string, custom_properties?: string[], - ): Promise> { + ): Promise> { try { const connection = await this.prisma.connections.findFirst({ where: { diff --git a/packages/api/src/ticketing/ticket/services/github/index.ts b/packages/api/src/ticketing/ticket/services/github/index.ts index 88c9f16c0..3113bb301 100644 --- a/packages/api/src/ticketing/ticket/services/github/index.ts +++ b/packages/api/src/ticketing/ticket/services/github/index.ts @@ -7,7 +7,6 @@ import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; import { ActionType, handleServiceError } from '@@core/utils/errors'; -import { OriginalTicketOutput } from '@@core/utils/types/original/original.ticketing'; import { ServiceRegistry } from '../registry.service'; import { GithubTicketInput, GithubTicketOutput } from './types'; @@ -66,7 +65,7 @@ export class GithubService implements ITicketService { async syncTickets( linkedUserId: string, custom_properties?: string[], - ): Promise> { + ): Promise> { try { const connection = await this.prisma.connections.findFirst({ where: { diff --git a/packages/api/src/ticketing/ticket/services/hubspot/index.ts b/packages/api/src/ticketing/ticket/services/hubspot/index.ts index 86d4665f4..c566eb1a1 100644 --- a/packages/api/src/ticketing/ticket/services/hubspot/index.ts +++ b/packages/api/src/ticketing/ticket/services/hubspot/index.ts @@ -7,9 +7,12 @@ import { ITicketService } from '@ticketing/ticket/types'; import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; import { ActionType, handleServiceError } from '@@core/utils/errors'; -import { OriginalTicketOutput } from '@@core/utils/types/original/original.ticketing'; import { ServiceRegistry } from '../registry.service'; -import { HubspotTicketInput, HubspotTicketOutput } from './types'; +import { + HubspotTicketInput, + HubspotTicketOutput, + commonHubspotProperties, +} from './types'; @Injectable() export class HubspotService implements ITicketService { @@ -35,9 +38,9 @@ export class HubspotService implements ITicketService { provider_slug: 'hubspot_t', }, }); - const dataBody = ticketData; + const dataBody = { properties: ticketData }; const resp = await axios.post( - `https://api2.frontapp.com/conversations`, + `https://api.hubapi.com/crm/v3/objects/tickets`, JSON.stringify(dataBody), { headers: { @@ -66,7 +69,7 @@ export class HubspotService implements ITicketService { async syncTickets( linkedUserId: string, custom_properties?: string[], - ): Promise> { + ): Promise> { try { const connection = await this.prisma.connections.findFirst({ where: { @@ -75,7 +78,16 @@ export class HubspotService implements ITicketService { }, }); - const resp = await axios.get('https://api2.frontapp.com/conversations', { + const commonPropertyNames = Object.keys(commonHubspotProperties); + const allProperties = [...commonPropertyNames, ...custom_properties]; + const baseURL = 'https://api.hubapi.com/crm/v3/objects/tickets/'; + + const queryString = allProperties + .map((prop) => `properties=${encodeURIComponent(prop)}`) + .join('&'); + + const url = `${baseURL}?${queryString}`; + const resp = await axios.get(url, { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.cryptoService.decrypt( @@ -86,7 +98,7 @@ export class HubspotService implements ITicketService { this.logger.log(`Synced hubspot tickets !`); return { - data: resp.data._results, + data: resp.data.results, message: 'Hubspot tickets retrieved', statusCode: 200, }; diff --git a/packages/api/src/ticketing/ticket/services/hubspot/types.ts b/packages/api/src/ticketing/ticket/services/hubspot/types.ts index 2c1bbfcbf..806e39651 100644 --- a/packages/api/src/ticketing/ticket/services/hubspot/types.ts +++ b/packages/api/src/ticketing/ticket/services/hubspot/types.ts @@ -1,5 +1,36 @@ export type HubspotTicketInput = { + subject: string; + hs_pipeline: string; + hubspot_owner_id: string; + hs_pipeline_stage: string; + hs_ticket_priority: string; +}; + +export type HubspotTicketOutput = { id: string; + properties: TicketProperties; + createdAt: string; + updatedAt: string; + archived: boolean; }; -export type HubspotTicketOutput = HubspotTicketInput; +type TicketProperties = { + createdate: string; + hs_lastmodifieddate: string; + hs_pipeline: string; + hs_pipeline_stage: string; + hs_ticket_priority: string; + hubspot_owner_id: string; + subject: string; + [key: string]: string; +}; + +export const commonHubspotProperties = { + createdate: '', + hs_lastmodifieddate: '', + hs_pipeline: '', + hs_pipeline_stage: '', + hs_ticket_priority: '', + hubspot_owner_id: '', + subject: '', +}; diff --git a/packages/api/src/ticketing/ticket/services/zendesk/index.ts b/packages/api/src/ticketing/ticket/services/zendesk/index.ts index c0b375ce3..947ed3bd4 100644 --- a/packages/api/src/ticketing/ticket/services/zendesk/index.ts +++ b/packages/api/src/ticketing/ticket/services/zendesk/index.ts @@ -12,7 +12,6 @@ import { ApiResponse } from '@@core/utils/types'; import axios from 'axios'; import { ActionType, handleServiceError } from '@@core/utils/errors'; import { EnvironmentService } from '@@core/environment/environment.service'; -import { OriginalTicketOutput } from '@@core/utils/types/original/original.ticketing'; import { ServiceRegistry } from '../registry.service'; @Injectable() @@ -73,7 +72,7 @@ export class ZendeskService implements ITicketService { async syncTickets( linkedUserId: string, custom_properties?: string[], - ): Promise> { + ): Promise> { try { const connection = await this.prisma.connections.findFirst({ where: { diff --git a/packages/api/src/ticketing/ticket/ticket.module.ts b/packages/api/src/ticketing/ticket/ticket.module.ts index 9fd39e2f2..fbe503b50 100644 --- a/packages/api/src/ticketing/ticket/ticket.module.ts +++ b/packages/api/src/ticketing/ticket/ticket.module.ts @@ -10,6 +10,9 @@ import { ZendeskService } from './services/zendesk'; import { BullModule } from '@nestjs/bull'; import { FieldMappingService } from '@@core/field-mapping/field-mapping.service'; import { ServiceRegistry } from './services/registry.service'; +import { HubspotService } from './services/hubspot'; +import { FrontService } from './services/front'; +import { GithubService } from './services/github'; @Module({ imports: [ @@ -29,6 +32,9 @@ import { ServiceRegistry } from './services/registry.service'; ServiceRegistry, /* PROVIDERS SERVICES */ ZendeskService, + HubspotService, + FrontService, + GithubService, ], exports: [SyncService], }) diff --git a/packages/api/swagger/swagger-spec.json b/packages/api/swagger/swagger-spec.json index 4e46c94c1..6ecb1c092 100644 --- a/packages/api/swagger/swagger-spec.json +++ b/packages/api/swagger/swagger-spec.json @@ -848,6 +848,210 @@ ] } }, + "/crm/deal": { + "get": { + "operationId": "getDeals", + "summary": "List a batch of Deals", + "parameters": [ + { + "name": "integrationId", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "linkedUserId", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "remoteData", + "required": false, + "in": "query", + "description": "Set to true to include data from the original Crm software.", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "crm/deal" + ] + }, + "post": { + "operationId": "addDeal", + "summary": "Create a Deal", + "description": "Create a deal in any supported Crm software", + "parameters": [ + { + "name": "integrationId", + "required": true, + "in": "header", + "description": "The integration ID", + "schema": { + "type": "string" + } + }, + { + "name": "linkedUserId", + "required": true, + "in": "header", + "description": "The linked user ID", + "schema": { + "type": "string" + } + }, + { + "name": "remoteData", + "required": false, + "in": "query", + "description": "Set to true to include data from the original Crm software.", + "schema": { + "type": "boolean" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnifiedDealInput" + } + } + } + }, + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "crm/deal" + ] + }, + "patch": { + "operationId": "updateDeal", + "summary": "Update a Deal", + "parameters": [ + { + "name": "id", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "crm/deal" + ] + } + }, + "/crm/deal/{id}": { + "get": { + "operationId": "getDeal", + "summary": "Retrieve a Deal", + "description": "Retrieve a deal from any connected Crm software", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "description": "id of the you want to retrive.", + "schema": { + "type": "string" + } + }, + { + "name": "remoteData", + "required": false, + "in": "query", + "description": "Set to true to include data from the original Crm software.", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "crm/deal" + ] + } + }, + "/crm/deal/batch": { + "post": { + "operationId": "addDeals", + "summary": "Add a batch of Deals", + "parameters": [ + { + "name": "integrationId", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "linkedUserId", + "required": true, + "in": "header", + "schema": { + "type": "string" + } + }, + { + "name": "remoteData", + "required": false, + "in": "query", + "description": "Set to true to include data from the original Crm software.", + "schema": { + "type": "boolean" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UnifiedDealInput" + } + } + } + } + }, + "responses": { + "201": { + "description": "" + } + }, + "tags": [ + "crm/deal" + ] + } + }, "/ticketing/ticket": { "get": { "operationId": "getTickets", @@ -1095,7 +1299,7 @@ "post": { "operationId": "addComment", "summary": "Create a Comment", - "description": "Create a ticket in any supported Ticketing software", + "description": "Create a comment in any supported Ticketing software", "parameters": [ { "name": "integrationId", @@ -1171,13 +1375,13 @@ "get": { "operationId": "getComment", "summary": "Retrieve a Comment", - "description": "Retrieve a ticket from any connected Ticketing software", + "description": "Retrieve a comment from any connected Ticketing software", "parameters": [ { "name": "id", "required": true, "in": "path", - "description": "id of the `ticket` you want to retrive.", + "description": "id of the `comment` you want to retrive.", "schema": { "type": "string" } @@ -1596,6 +1800,10 @@ "field_mappings" ] }, + "UnifiedDealInput": { + "type": "object", + "properties": {} + }, "UnifiedTicketInput": { "type": "object", "properties": {