From fec82f06da694c9c8639cdfe06f2241b4723f6f3 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Tue, 13 May 2025 09:54:34 -0400 Subject: [PATCH 1/9] feat(zendesk-api-service): add function to fetch voice lines --- .../services/zendesk-api-service.spec.ts | 37 +++++++++++++++ src/models/zendesk-api.ts | 47 +++++++++++++++++++ src/services/zendesk-api-service.ts | 21 +++++++-- 3 files changed, 101 insertions(+), 4 deletions(-) diff --git a/__tests__/services/zendesk-api-service.spec.ts b/__tests__/services/zendesk-api-service.spec.ts index e1c4606..fe18f20 100644 --- a/__tests__/services/zendesk-api-service.spec.ts +++ b/__tests__/services/zendesk-api-service.spec.ts @@ -439,6 +439,43 @@ describe("ZendeskService", () => { expect(result).toEqual(locales); }); }); + + describe("getVoiceLines", () => { + it("should call the API and return the voice lines", async () => { + const lines = [{ name: "line1" }]; + requestMock.mockResolvedValueOnce({ lines }); + + const result = await service.getVoiceLines(); + + expect(requestMock).toHaveBeenCalledWith(`/api/v2/channels/voice/lines`); + expect(result).toEqual(lines); + }); + + it("should continue calling the API until next_page disappears", async () => { + const lines = [{ name: "line1" }]; + requestMock + .mockResolvedValueOnce({ lines, next_page: "next_page" }) + .mockResolvedValueOnce({ lines: [] }); + + const result = await service.getVoiceLines(); + + expect(requestMock).toHaveBeenCalledTimes(2); + expect(requestMock).toHaveBeenNthCalledWith(1, `/api/v2/channels/voice/lines`); + expect(requestMock).toHaveBeenNthCalledWith(2, "next_page"); + expect(result).toEqual(lines); + }); + + it("should only call the API one time with fetchAllLines set to false", async () => { + const lines = [{ name: "line1" }]; + requestMock.mockResolvedValueOnce({ lines, next_page: "next_page" }); + + const result = await service.getVoiceLines(false); + + expect(requestMock).toHaveBeenCalledTimes(1); + expect(requestMock).toHaveBeenCalledWith(`/api/v2/channels/voice/lines`); + expect(result).toEqual(lines); + }); + }); }); }); }); diff --git a/src/models/zendesk-api.ts b/src/models/zendesk-api.ts index 7121852..239d2a2 100644 --- a/src/models/zendesk-api.ts +++ b/src/models/zendesk-api.ts @@ -54,6 +54,53 @@ export interface IOrganizationsResults extends IZendeskResponse { organizations: IZendeskOrganizations[]; } +export interface ILinesResults extends IZendeskResponse { + lines: IZendeskLines[]; +} + +export interface IZendeskLines { + capabilities: { + emergency_address: boolean; + mms: boolean; + sms: boolean; + voice: boolean; + }; + categorised_greetings: { + 1: string; + 2: string; + }; + categorised_greetings_with_sub_settings: { + 1: { + voicemail_off_inside_business_hours: string; + voicemail_off_outside_business_hours: string; + voicemail_on_inside_business_hours: string; + }; + 2: { + voicemail_off: string; + voicemail_on: string; + }; + }; + country_code: string; + created_at: string; + default_greeting_ids: string[]; + default_group_id: number; + display_number: string; + external: boolean; + greeting_ids: number[]; + group_ids: number[]; + id: number; + line_type: string; + location: string; + name: string; + nickname: string; + // eslint-disable-next-line id-denylist + number: string; + recorded: boolean; + sms_group_id: number; + toll_free: boolean; + transcription: boolean; +} + export interface ILocalesResults { locales: IZendeskLocale[]; } diff --git a/src/services/zendesk-api-service.ts b/src/services/zendesk-api-service.ts index 40a6012..9927f68 100644 --- a/src/services/zendesk-api-service.ts +++ b/src/services/zendesk-api-service.ts @@ -18,7 +18,10 @@ import { IZendeskTag, IZendeskLocale, IZendeskGroup, - IZendeskOrganizations + IZendeskOrganizations, + ILinesResults, + IZendeskLines, + IZendeskResponse } from "@models/index"; import { convertContentMessageToHtml } from "@utils/convert-content-message-to-html"; import { getFromClient } from "@utils/get-from-client"; @@ -40,16 +43,15 @@ export class ZendeskApiService { * @param extractArrayFn Function to extract the array of items from the response. * @returns A promise resolving to a flattened array of all items. */ - private async fetchAllPaginatedResults( + private async fetchAllPaginatedResults( url: string, fetchAll: boolean, extractArrayFn: (response: TResponse) => TItem[] ): Promise { const results: TResponse[] = [await this.client.request(url)]; - if (fetchAll) { while (true) { - const nextPage = (results[results.length - 1] as TResponse & { next_page?: string }).next_page; + const nextPage = results[results.length - 1].next_page; if (!nextPage) break; results.push(await this.client.request(nextPage)); } @@ -253,4 +255,15 @@ export class ZendeskApiService { return results.locales; } + + /** + * Fetch all voice lines + */ + public async getVoiceLines(fetchAllLines = true): Promise { + return this.fetchAllPaginatedResults( + `/api/v2/channels/voice/lines`, + fetchAllLines, + (response) => response.lines + ); + } } From 543a932c186cc4d403a99ff60f24f7a33ac5809d Mon Sep 17 00:00:00 2001 From: tristan-vu Date: Tue, 13 May 2025 13:55:28 +0000 Subject: [PATCH 2/9] [BOT] Bump version from 0.2.12 to 0.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1874022..23edcd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zendesk/zaf-toolbox", - "version": "0.2.12", + "version": "0.3.0", "description": "A toolbox for ZAF application built with 🩷 by Zendesk Labs", "main": "lib/src/index.js", "types": "lib/src/index.d.ts", From b78521866273bcf52ac7722d8f1869ee6c7333e7 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Tue, 13 May 2025 10:55:12 -0400 Subject: [PATCH 3/9] feat(sunshine-conversation): update twilio type --- src/models/sunshine-conversation.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/models/sunshine-conversation.ts b/src/models/sunshine-conversation.ts index 1767e08..8cb0f3f 100644 --- a/src/models/sunshine-conversation.ts +++ b/src/models/sunshine-conversation.ts @@ -432,7 +432,7 @@ export type IIntegration = | IIntegrationIos | IIntegrationMessageBirds | IIntegrationMessenger - | IIntegrationTwillio + | IIntegrationTwilio | IIntegrationTwitter | IIntegrationWeb | IIntegrationWhatsApp @@ -548,8 +548,12 @@ export interface IIntegrationMessenger extends IIntegrationBase { pageName?: string; } -export interface IIntegrationTwillio extends IIntegrationBase { +export interface IIntegrationTwilio extends IIntegrationBase { type: UserChannelTypes.Twilio; + phoneNumber: string; +} + +export interface IIntegrationSuncoTwilio extends IIntegrationTwilio { /** * Twilio Account SID. */ @@ -558,7 +562,6 @@ export interface IIntegrationTwillio extends IIntegrationBase { * SID for specific phone number. One of messagingServiceSid or phoneNumberSid must be provided when creating a Twilio integration */ phoneNumberSid: string; - phoneNumber: string; /** * SID for specific messaging service. One of messagingServiceSid or phoneNumberSid must be provided when creating a Twilio integration. */ From b726806f1fb8db383f68172eeac078461e19c14b Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Tue, 13 May 2025 10:57:46 -0400 Subject: [PATCH 4/9] feat(sunshine-conversations): add capabilities --- src/models/sunshine-conversation.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/models/sunshine-conversation.ts b/src/models/sunshine-conversation.ts index 8cb0f3f..e9b03a1 100644 --- a/src/models/sunshine-conversation.ts +++ b/src/models/sunshine-conversation.ts @@ -551,6 +551,8 @@ export interface IIntegrationMessenger extends IIntegrationBase { export interface IIntegrationTwilio extends IIntegrationBase { type: UserChannelTypes.Twilio; phoneNumber: string; + sms: boolean; + mms: boolean; } export interface IIntegrationSuncoTwilio extends IIntegrationTwilio { From c6f1490e7e73ccdc5ac9c5c2425c784e451ba747 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Tue, 13 May 2025 11:01:43 -0400 Subject: [PATCH 5/9] chore(sunshine-conversation): refactor type --- .../sunshine-conversation-api-service.spec.ts | 27 +++++++++---------- src/models/sunshine-conversation.ts | 3 +++ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/__tests__/services/sunshine-conversation-api-service.spec.ts b/__tests__/services/sunshine-conversation-api-service.spec.ts index 572bf07..34f2817 100644 --- a/__tests__/services/sunshine-conversation-api-service.spec.ts +++ b/__tests__/services/sunshine-conversation-api-service.spec.ts @@ -6,6 +6,7 @@ import { Capabilities, IAuthor, IContent, + IIntegrationSuncoTwilio, IIntegrationWhatsApp, ISendNotificationPayload, IServiceConfig, @@ -473,21 +474,19 @@ describe("SunshineConversationApiService", () => { } }; + const notification: IIntegrationSuncoTwilio = { + id: "id", + status: "status", + "type": UserChannelTypes.Twilio, + accountSid: "accountSid", + phoneNumberSid: "phoneNumberSid", + messagingServiceSid: "messagingServiceSid", + displayName: "Twilio", + phoneNumber: "+12345678900" + }; + // Sending twilio notification - await sunshineConversationApiService.sendNotification( - { - id: "id", - status: "status", - "type": UserChannelTypes.Twilio, - accountSid: "accountSid", - phoneNumberSid: "phoneNumberSid", - messagingServiceSid: "messagingServiceSid", - displayName: "Twilio", - phoneNumber: "+12345678900" - }, - phoneNumberSample, - contentSample - ); + await sunshineConversationApiService.sendNotification(notification, phoneNumberSample, contentSample); expect(client.request).toHaveBeenCalledWith(options); }); diff --git a/src/models/sunshine-conversation.ts b/src/models/sunshine-conversation.ts index e9b03a1..9c0c99a 100644 --- a/src/models/sunshine-conversation.ts +++ b/src/models/sunshine-conversation.ts @@ -551,6 +551,9 @@ export interface IIntegrationMessenger extends IIntegrationBase { export interface IIntegrationTwilio extends IIntegrationBase { type: UserChannelTypes.Twilio; phoneNumber: string; +} + +export interface IIntegrationTwilioTalk extends IIntegrationTwilio { sms: boolean; mms: boolean; } From f44741edb42ad4ba40c9028abe3338ec4a56b196 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Tue, 13 May 2025 11:08:32 -0400 Subject: [PATCH 6/9] chore(sunshine-conversation): add isTalk --- src/models/sunshine-conversation.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/models/sunshine-conversation.ts b/src/models/sunshine-conversation.ts index 9c0c99a..c6582a6 100644 --- a/src/models/sunshine-conversation.ts +++ b/src/models/sunshine-conversation.ts @@ -551,6 +551,7 @@ export interface IIntegrationMessenger extends IIntegrationBase { export interface IIntegrationTwilio extends IIntegrationBase { type: UserChannelTypes.Twilio; phoneNumber: string; + isTalk?: boolean; } export interface IIntegrationTwilioTalk extends IIntegrationTwilio { From e392777bd6bfd8cf96777fbf3d53d668bd722938 Mon Sep 17 00:00:00 2001 From: Tristan Vu <102526684+tristan-vu@users.noreply.github.com> Date: Tue, 13 May 2025 11:35:18 -0400 Subject: [PATCH 7/9] Update src/models/zendesk-api.ts Co-authored-by: Cuneyt Celebican --- src/models/zendesk-api.ts | 108 ++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/src/models/zendesk-api.ts b/src/models/zendesk-api.ts index 239d2a2..423172e 100644 --- a/src/models/zendesk-api.ts +++ b/src/models/zendesk-api.ts @@ -55,52 +55,72 @@ export interface IOrganizationsResults extends IZendeskResponse { } export interface ILinesResults extends IZendeskResponse { - lines: IZendeskLines[]; -} - -export interface IZendeskLines { - capabilities: { - emergency_address: boolean; - mms: boolean; - sms: boolean; - voice: boolean; - }; - categorised_greetings: { - 1: string; - 2: string; - }; - categorised_greetings_with_sub_settings: { - 1: { - voicemail_off_inside_business_hours: string; - voicemail_off_outside_business_hours: string; - voicemail_on_inside_business_hours: string; - }; - 2: { - voicemail_off: string; - voicemail_on: string; - }; - }; - country_code: string; - created_at: string; - default_greeting_ids: string[]; - default_group_id: number; - display_number: string; - external: boolean; - greeting_ids: number[]; - group_ids: number[]; - id: number; - line_type: string; - location: string; - name: string; - nickname: string; - // eslint-disable-next-line id-denylist - number: string; - recorded: boolean; - sms_group_id: number; - toll_free: boolean; - transcription: boolean; + lines: Line[]; + next_page: string | null; + previous_page: string | null; + count: number; +} + +interface LineBase { + id: number; + nickname: string; + priority: number; + default_group_id: number | null; + line_type: string; + transcription: boolean; + recorded: boolean; + call_recording_consent: string; + group_ids: number[]; + greeting_ids: string[]; + default_greeting_ids: string[]; + categorised_greetings_with_sub_settings: CategorisedGreetingsWithSubSettings; + schedule_id: number | null; + created_at: string; +} + +export interface DigitalLine extends LineBase { + line_type: "digital"; + brand_id: number; + line_id: string; + outbound_number: string | null; } +export interface PhoneLine extends LineBase { + line_type: "phone"; + country_code: string; + external: boolean; + number: string; + name: string; + display_number: string; + location: string; + toll_free: boolean; + categorised_greetings: CategorisedGreetings; + sms_group_id: number | null; + capabilities: Capabilities; + sms_enabled: boolean; + voice_enabled: boolean; + outbound_enabled: boolean; + ivr_id: number | null; + failover_number: string | null; +} + +interface Capabilities { + sms: boolean; + mms: boolean; + voice: boolean; + emergency_address: boolean; +} + +interface CategorisedGreetings { + [key: string]: string; +} + +interface CategorisedGreetingsWithSubSettings { + [key: string]: string | { [subKey: string]: string }; +} + +export type Line = DigitalLine | PhoneLine; + export interface ILocalesResults { locales: IZendeskLocale[]; } From 522fc25b8fbdf975fea83daac5652ea8de22f8f6 Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Tue, 13 May 2025 11:42:35 -0400 Subject: [PATCH 8/9] chore(zendesk-api): update line interface --- src/models/zendesk-api.ts | 87 +++++++++++++---------------- src/services/zendesk-api-service.ts | 8 +-- 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/src/models/zendesk-api.ts b/src/models/zendesk-api.ts index 423172e..84d7cd4 100644 --- a/src/models/zendesk-api.ts +++ b/src/models/zendesk-api.ts @@ -61,62 +61,55 @@ export interface ILinesResults extends IZendeskResponse { count: number; } -interface LineBase { - id: number; - nickname: string; - priority: number; - default_group_id: number | null; - line_type: string; - transcription: boolean; - recorded: boolean; - call_recording_consent: string; - group_ids: number[]; - greeting_ids: string[]; - default_greeting_ids: string[]; - categorised_greetings_with_sub_settings: CategorisedGreetingsWithSubSettings; - schedule_id: number | null; - created_at: string; +export interface LineBase { + id: number; + nickname: string; + priority: number; + default_group_id: number | null; + line_type: string; + transcription: boolean; + recorded: boolean; + call_recording_consent: string; + group_ids: number[]; + greeting_ids: string[]; + default_greeting_ids: string[]; + categorised_greetings_with_sub_settings: Record>; + schedule_id: number | null; + created_at: string; } export interface DigitalLine extends LineBase { - line_type: "digital"; - brand_id: number; - line_id: string; - outbound_number: string | null; + line_type: "digital"; + brand_id: number; + line_id: string; + outbound_number: string | null; } export interface PhoneLine extends LineBase { - line_type: "phone"; - country_code: string; - external: boolean; - number: string; - name: string; - display_number: string; - location: string; - toll_free: boolean; - categorised_greetings: CategorisedGreetings; - sms_group_id: number | null; - capabilities: Capabilities; - sms_enabled: boolean; - voice_enabled: boolean; - outbound_enabled: boolean; - ivr_id: number | null; - failover_number: string | null; + line_type: "phone"; + country_code: string; + external: boolean; + // eslint-disable-next-line id-denylist + number: string; + name: string; + display_number: string; + location: string; + toll_free: boolean; + categorised_greetings: Record; + sms_group_id: number | null; + capabilities: Capabilities; + sms_enabled: boolean; + voice_enabled: boolean; + outbound_enabled: boolean; + ivr_id: number | null; + failover_number: string | null; } interface Capabilities { - sms: boolean; - mms: boolean; - voice: boolean; - emergency_address: boolean; -} - -interface CategorisedGreetings { - [key: string]: string; -} - -interface CategorisedGreetingsWithSubSettings { - [key: string]: string | { [subKey: string]: string }; + sms: boolean; + mms: boolean; + voice: boolean; + emergency_address: boolean; } export type Line = DigitalLine | PhoneLine; diff --git a/src/services/zendesk-api-service.ts b/src/services/zendesk-api-service.ts index 9927f68..11655aa 100644 --- a/src/services/zendesk-api-service.ts +++ b/src/services/zendesk-api-service.ts @@ -20,8 +20,8 @@ import { IZendeskGroup, IZendeskOrganizations, ILinesResults, - IZendeskLines, - IZendeskResponse + IZendeskResponse, + LineBase } from "@models/index"; import { convertContentMessageToHtml } from "@utils/convert-content-message-to-html"; import { getFromClient } from "@utils/get-from-client"; @@ -259,8 +259,8 @@ export class ZendeskApiService { /** * Fetch all voice lines */ - public async getVoiceLines(fetchAllLines = true): Promise { - return this.fetchAllPaginatedResults( + public async getVoiceLines(fetchAllLines = true): Promise { + return this.fetchAllPaginatedResults( `/api/v2/channels/voice/lines`, fetchAllLines, (response) => response.lines From d905712c78ecaa0be2c2c64ca0bb49e9b73ea5fb Mon Sep 17 00:00:00 2001 From: Tristan Vu Date: Wed, 14 May 2025 10:54:37 -0400 Subject: [PATCH 9/9] chore(zendesk-api): update interface --- src/models/zendesk-api.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/models/zendesk-api.ts b/src/models/zendesk-api.ts index 84d7cd4..97fa907 100644 --- a/src/models/zendesk-api.ts +++ b/src/models/zendesk-api.ts @@ -56,9 +56,6 @@ export interface IOrganizationsResults extends IZendeskResponse { export interface ILinesResults extends IZendeskResponse { lines: Line[]; - next_page: string | null; - previous_page: string | null; - count: number; } export interface LineBase {