@ikatec/digisac-api-sdk 4.10.0 → 4.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apis/categories/types.d.ts +1 -0
- package/dist/apis/categories/types.d.ts.map +1 -1
- package/dist/apis/contactBlockLists/ContactBlockListsApi.cjs +28 -0
- package/dist/apis/contactBlockLists/ContactBlockListsApi.cjs.map +1 -1
- package/dist/apis/contactBlockLists/ContactBlockListsApi.d.ts +24 -0
- package/dist/apis/contactBlockLists/ContactBlockListsApi.d.ts.map +1 -1
- package/dist/apis/contactBlockLists/ContactBlockListsApi.mjs +28 -0
- package/dist/apis/contactBlockLists/ContactBlockListsApi.mjs.map +1 -1
- package/dist/apis/contactBlockLists/types.d.ts +6 -1
- package/dist/apis/contactBlockLists/types.d.ts.map +1 -1
- package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.cjs +31 -1
- package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.cjs.map +1 -1
- package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.d.ts +28 -2
- package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.d.ts.map +1 -1
- package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.mjs +31 -1
- package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.mjs.map +1 -1
- package/dist/apis/contactBlockListsControls/types.d.ts +7 -0
- package/dist/apis/contactBlockListsControls/types.d.ts.map +1 -1
- package/dist/apis/distribution/DistributionApi.cjs +25 -0
- package/dist/apis/distribution/DistributionApi.cjs.map +1 -1
- package/dist/apis/distribution/DistributionApi.d.ts +20 -1
- package/dist/apis/distribution/DistributionApi.d.ts.map +1 -1
- package/dist/apis/distribution/DistributionApi.mjs +25 -0
- package/dist/apis/distribution/DistributionApi.mjs.map +1 -1
- package/dist/apis/distribution/types.d.ts +18 -1
- package/dist/apis/distribution/types.d.ts.map +1 -1
- package/dist/apis/integrations/IntegrationsApi.cjs +17 -0
- package/dist/apis/integrations/IntegrationsApi.cjs.map +1 -1
- package/dist/apis/integrations/IntegrationsApi.d.ts +15 -2
- package/dist/apis/integrations/IntegrationsApi.d.ts.map +1 -1
- package/dist/apis/integrations/IntegrationsApi.mjs +17 -0
- package/dist/apis/integrations/IntegrationsApi.mjs.map +1 -1
- package/dist/apis/integrations/types.d.ts +15 -6
- package/dist/apis/integrations/types.d.ts.map +1 -1
- package/dist/apis/interactiveMessages/InteractiveMessagesApi.cjs +15 -0
- package/dist/apis/interactiveMessages/InteractiveMessagesApi.cjs.map +1 -1
- package/dist/apis/interactiveMessages/InteractiveMessagesApi.d.ts +13 -0
- package/dist/apis/interactiveMessages/InteractiveMessagesApi.d.ts.map +1 -1
- package/dist/apis/interactiveMessages/InteractiveMessagesApi.mjs +15 -0
- package/dist/apis/interactiveMessages/InteractiveMessagesApi.mjs.map +1 -1
- package/dist/apis/interactiveMessages/types.d.ts +8 -2
- package/dist/apis/interactiveMessages/types.d.ts.map +1 -1
- package/dist/apis/me/MeApi.cjs +36 -29
- package/dist/apis/me/MeApi.cjs.map +1 -1
- package/dist/apis/me/MeApi.d.ts +33 -22
- package/dist/apis/me/MeApi.d.ts.map +1 -1
- package/dist/apis/me/MeApi.mjs +36 -29
- package/dist/apis/me/MeApi.mjs.map +1 -1
- package/dist/apis/me/types.d.ts +67 -4
- package/dist/apis/me/types.d.ts.map +1 -1
- package/dist/apis/transcripts/TranscriptsApi.cjs +12 -2
- package/dist/apis/transcripts/TranscriptsApi.cjs.map +1 -1
- package/dist/apis/transcripts/TranscriptsApi.d.ts +11 -5
- package/dist/apis/transcripts/TranscriptsApi.d.ts.map +1 -1
- package/dist/apis/transcripts/TranscriptsApi.mjs +12 -2
- package/dist/apis/transcripts/TranscriptsApi.mjs.map +1 -1
- package/dist/apis/transcripts/types.d.ts +4 -0
- package/dist/apis/transcripts/types.d.ts.map +1 -1
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.cjs +52 -0
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.cjs.map +1 -1
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.d.ts +45 -2
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.d.ts.map +1 -1
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.mjs +52 -0
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.mjs.map +1 -1
- package/dist/apis/whatsappBusinessTemplates/types.d.ts +34 -1
- package/dist/apis/whatsappBusinessTemplates/types.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.cjs +3 -1
- package/dist/core/BaseCrudApi.cjs.map +1 -1
- package/dist/core/BaseCrudApi.d.ts +6 -4
- package/dist/core/BaseCrudApi.d.ts.map +1 -1
- package/dist/core/BaseCrudApi.mjs +3 -1
- package/dist/core/BaseCrudApi.mjs.map +1 -1
- package/dist/core/types.d.ts +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/apis/categories/types.ts +1 -0
- package/src/apis/contactBlockLists/ContactBlockListsApi.test.ts +57 -0
- package/src/apis/contactBlockLists/ContactBlockListsApi.ts +44 -0
- package/src/apis/contactBlockLists/types.ts +6 -1
- package/src/apis/contactBlockListsControls/ContactBlockListsControlsApi.test.ts +48 -0
- package/src/apis/contactBlockListsControls/ContactBlockListsControlsApi.ts +49 -2
- package/src/apis/contactBlockListsControls/types.ts +7 -0
- package/src/apis/distribution/DistributionApi.test.ts +65 -0
- package/src/apis/distribution/DistributionApi.ts +39 -1
- package/src/apis/distribution/types.ts +17 -1
- package/src/apis/integrations/IntegrationsApi.test.ts +42 -0
- package/src/apis/integrations/IntegrationsApi.ts +35 -2
- package/src/apis/integrations/types.ts +25 -6
- package/src/apis/interactiveMessages/InteractiveMessagesApi.test.ts +59 -0
- package/src/apis/interactiveMessages/InteractiveMessagesApi.ts +26 -0
- package/src/apis/interactiveMessages/types.ts +8 -2
- package/src/apis/me/MeApi.test.ts +44 -0
- package/src/apis/me/MeApi.ts +83 -28
- package/src/apis/me/types.ts +75 -4
- package/src/apis/payloads.test-d.ts +94 -2
- package/src/apis/transcripts/TranscriptsApi.test.ts +14 -21
- package/src/apis/transcripts/TranscriptsApi.ts +15 -6
- package/src/apis/transcripts/types.ts +4 -0
- package/src/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.test.ts +86 -0
- package/src/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.ts +75 -1
- package/src/apis/whatsappBusinessTemplates/types.ts +38 -1
- package/src/core/BaseCrudApi.test-d.ts +11 -1
- package/src/core/BaseCrudApi.ts +15 -6
- package/src/core/types.ts +4 -2
|
@@ -18,6 +18,10 @@ import type {
|
|
|
18
18
|
import { CardsApi } from './cards/CardsApi'
|
|
19
19
|
import { ContactsApi } from './contacts/ContactsApi'
|
|
20
20
|
import { InteractiveMessagesApi } from './interactiveMessages/InteractiveMessagesApi'
|
|
21
|
+
import { MeApi } from './me/MeApi'
|
|
22
|
+
import type { AccountAmounts, MeStatusResponse } from './me/types'
|
|
23
|
+
import { TranscriptsApi } from './transcripts/TranscriptsApi'
|
|
24
|
+
import { WhatsappBusinessTemplatesApi } from './whatsappBusinessTemplates/WhatsappBusinessTemplatesApi'
|
|
21
25
|
import type {
|
|
22
26
|
ChannelRedirect,
|
|
23
27
|
CtaUrlInteractiveMessageContent,
|
|
@@ -54,6 +58,8 @@ import type { SummarizeTicketResult } from './contacts/types'
|
|
|
54
58
|
import { CustomFieldsApi } from './customFields/CustomFieldsApi'
|
|
55
59
|
import { DepartmentsApi } from './departments/DepartmentsApi'
|
|
56
60
|
import type { Department } from './departments/types'
|
|
61
|
+
import type { Distribution } from './distribution/types'
|
|
62
|
+
import type { ContactBlockList } from './contactBlockLists/types'
|
|
57
63
|
import { DistributionApi } from './distribution/DistributionApi'
|
|
58
64
|
import { FilesApi } from './files/FilesApi'
|
|
59
65
|
import type { CreateToUploadResult, DeleteFileResult, File } from './files/types'
|
|
@@ -68,7 +74,7 @@ import { NotificationsApi } from './notifications/NotificationsApi'
|
|
|
68
74
|
import { OrganizationsApi } from './organizations/OrganizationsApi'
|
|
69
75
|
import { PeopleApi } from './people/PeopleApi'
|
|
70
76
|
import { ServicesApi } from './services/ServicesApi'
|
|
71
|
-
import type { UpdateUserPayload } from './users/types'
|
|
77
|
+
import type { UpdateUserPayload, User } from './users/types'
|
|
72
78
|
import { WebhooksApi } from './webhooks/WebhooksApi'
|
|
73
79
|
|
|
74
80
|
declare const client: ApiClient
|
|
@@ -208,7 +214,10 @@ expectError(
|
|
|
208
214
|
)
|
|
209
215
|
|
|
210
216
|
declare const interactiveMessage: InteractiveMessage
|
|
211
|
-
|
|
217
|
+
// `type` is not a field of the backend (deprecated, optional): the type is `interactive.type`.
|
|
218
|
+
expectType<'list' | 'button' | 'cta_url' | 'request_contact_info' | undefined>(
|
|
219
|
+
interactiveMessage.type,
|
|
220
|
+
)
|
|
212
221
|
if (interactiveMessage.interactive.type === 'cta_url') {
|
|
213
222
|
expectType<CtaUrlInteractiveMessageContent>(interactiveMessage.interactive)
|
|
214
223
|
expectType<string>(interactiveMessage.interactive.action.parameters.url)
|
|
@@ -545,6 +554,89 @@ expectType<Promise<'OK'>>(new ContactBlockListsControlsApi(client).deleteById('c
|
|
|
545
554
|
expectType<Promise<boolean>>(new RolesApi(client).deleteById('r-1'))
|
|
546
555
|
expectType<Promise<void>>(new AcceptanceTermsApi(client).deleteById('t-1'))
|
|
547
556
|
expectType<Promise<void>>(new DistributionApi(client).deleteById('d-1'))
|
|
557
|
+
|
|
558
|
+
// ── QARC-1009: /me, transcripts, interactive messages, templates ─────
|
|
559
|
+
|
|
560
|
+
expectType<Promise<'OK'>>(new MeApi(client).activateOtpAuth({ 'x-digisac-otp': '123456' }))
|
|
561
|
+
expectType<Promise<MeStatusResponse>>(
|
|
562
|
+
new MeApi(client).updateStatus({ status: 'offline', client: 'web' }),
|
|
563
|
+
)
|
|
564
|
+
expectType<Promise<AccountAmounts>>(
|
|
565
|
+
new MeApi(client).getAccountAmounts({ filter: ['users.amounts'] }),
|
|
566
|
+
)
|
|
567
|
+
expectType<Promise<User>>(new MeApi(client).getMe({ include: ['account'] }))
|
|
568
|
+
expectError(new MeApi(client).updateStatus({ status: 'offline', client: 'desktop' }))
|
|
569
|
+
expectType<Promise<void>>(new TranscriptsApi(client).transcribe('m-1'))
|
|
570
|
+
expectType<Promise<{ count: number }>>(new InteractiveMessagesApi(client).count())
|
|
571
|
+
expectType<Promise<{ count: number }>>(new WhatsappBusinessTemplatesApi(client).count())
|
|
572
|
+
expectType<Promise<'OK'>>(new WhatsappBusinessTemplatesApi(client).refreshTemplates())
|
|
573
|
+
expectError(
|
|
574
|
+
new WhatsappBusinessTemplatesApi(client).create({
|
|
575
|
+
name: 'qa',
|
|
576
|
+
internalName: 'QA',
|
|
577
|
+
components: [],
|
|
578
|
+
serviceId: 's-1',
|
|
579
|
+
messageType: 'text_only',
|
|
580
|
+
language: 'pt_BR',
|
|
581
|
+
category: 'UTILITY',
|
|
582
|
+
}),
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
// ── create/updateById: what each route really answers (QARC-1008) ─────
|
|
586
|
+
|
|
587
|
+
expectType<Promise<Distribution | undefined>>(
|
|
588
|
+
new DistributionApi(client).create({
|
|
589
|
+
name: 'd',
|
|
590
|
+
maxNum: 1,
|
|
591
|
+
redistributeAll: false,
|
|
592
|
+
departments: [{ id: 'dep-1' }],
|
|
593
|
+
}),
|
|
594
|
+
)
|
|
595
|
+
expectType<Promise<Distribution | Department[]>>(
|
|
596
|
+
new DistributionApi(client).updateById('d-1', {
|
|
597
|
+
name: 'd',
|
|
598
|
+
roles: [{ id: 'r-1' }],
|
|
599
|
+
departments: [],
|
|
600
|
+
}),
|
|
601
|
+
)
|
|
602
|
+
expectType<Promise<Record<string, never>>>(
|
|
603
|
+
new IntegrationsApi(client).create({ type: 'menu_button', text: 'x' }),
|
|
604
|
+
)
|
|
605
|
+
expectType<Promise<Record<string, never>>>(
|
|
606
|
+
new IntegrationsApi(client).updateById('i-1', { text: 'x' }),
|
|
607
|
+
)
|
|
608
|
+
expectType<Promise<void>>(
|
|
609
|
+
new ContactBlockListsControlsApi(client).create({
|
|
610
|
+
reason: 'r',
|
|
611
|
+
servicesIds: ['s-1'],
|
|
612
|
+
contactBlockListsIds: ['l-1'],
|
|
613
|
+
action: 'block',
|
|
614
|
+
revertExceptions: false,
|
|
615
|
+
}),
|
|
616
|
+
)
|
|
617
|
+
expectType<Promise<{ count: number }>>(
|
|
618
|
+
new ContactBlockListsApi(client).count({ where: { name: 'x' } }),
|
|
619
|
+
)
|
|
620
|
+
expectType<Promise<{ count: number }>>(new ContactBlockListsControlsApi(client).count())
|
|
621
|
+
expectType<Promise<Paginated<ContactBlockList>>>(
|
|
622
|
+
new ContactBlockListsApi(client).getMany({ include: ['importFile'] }),
|
|
623
|
+
)
|
|
624
|
+
expectError(
|
|
625
|
+
new DistributionApi(client).create({
|
|
626
|
+
name: 'd',
|
|
627
|
+
maxNum: 1,
|
|
628
|
+
redistributeAll: false,
|
|
629
|
+
departments: ['dep-1'],
|
|
630
|
+
}),
|
|
631
|
+
)
|
|
632
|
+
expectError(
|
|
633
|
+
new ContactBlockListsControlsApi(client).create({
|
|
634
|
+
reason: 'r',
|
|
635
|
+
servicesIds: [],
|
|
636
|
+
contactBlockListsIds: [],
|
|
637
|
+
action: 'remove',
|
|
638
|
+
}),
|
|
639
|
+
)
|
|
548
640
|
expectType<Promise<false>>(new KnowledgeBaseApi(client).deleteById('k-1'))
|
|
549
641
|
expectType<Promise<false>>(new KnowledgeBaseItemApi(client).deleteById('k-1'))
|
|
550
642
|
expectType<Promise<DeleteFileResult>>(new FilesApi(client).deleteById('f-1'))
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { TranscriptsApi } from './TranscriptsApi'
|
|
3
3
|
import type { ApiClient } from '../../core/ApiClient'
|
|
4
|
-
import type { TranscriptResponse } from './types'
|
|
5
4
|
|
|
6
5
|
// ── Mock client factory ──────────────────────────────────────────────
|
|
7
6
|
|
|
@@ -28,36 +27,30 @@ describe('TranscriptsApi', () => {
|
|
|
28
27
|
api = new TranscriptsApi(client)
|
|
29
28
|
})
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const response: TranscriptResponse = {
|
|
36
|
-
id: 'msg-1',
|
|
37
|
-
isTranscribing: true,
|
|
38
|
-
transcribeError: false,
|
|
39
|
-
}
|
|
40
|
-
vi.mocked(client.get).mockResolvedValue(response)
|
|
41
|
-
const result = await api.getById('msg-1')
|
|
30
|
+
describe('transcribe', () => {
|
|
31
|
+
it('calls client.get with /transcripts/:messageId and resolves the empty answer', async () => {
|
|
32
|
+
vi.mocked(client.get).mockResolvedValue(undefined)
|
|
33
|
+
await expect(api.transcribe('msg-1')).resolves.toBeUndefined()
|
|
42
34
|
expect(client.get).toHaveBeenCalledWith('/transcripts/msg-1', undefined)
|
|
43
|
-
expect(result).toEqual(response)
|
|
44
35
|
})
|
|
45
36
|
|
|
46
37
|
it('forwards custom headers', async () => {
|
|
47
|
-
const response: TranscriptResponse = {
|
|
48
|
-
id: 'msg-2',
|
|
49
|
-
isTranscribing: false,
|
|
50
|
-
transcribeError: true,
|
|
51
|
-
}
|
|
52
|
-
vi.mocked(client.get).mockResolvedValue(response)
|
|
53
38
|
const headers = { 'X-Custom': 'h' }
|
|
54
|
-
await api.
|
|
39
|
+
await api.transcribe('msg-2', headers)
|
|
55
40
|
expect(client.get).toHaveBeenCalledWith('/transcripts/msg-2', headers)
|
|
56
41
|
})
|
|
57
42
|
|
|
58
43
|
it('propagates errors from client.get', async () => {
|
|
59
44
|
vi.mocked(client.get).mockRejectedValue(new Error('Not found'))
|
|
60
|
-
await expect(api.
|
|
45
|
+
await expect(api.transcribe('missing')).rejects.toThrow('Not found')
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe('getById (deprecated)', () => {
|
|
50
|
+
it('starts the transcription through the same route', async () => {
|
|
51
|
+
vi.mocked(client.get).mockResolvedValue(undefined)
|
|
52
|
+
await expect(api.getById('msg-3')).resolves.toBeUndefined()
|
|
53
|
+
expect(client.get).toHaveBeenCalledWith('/transcripts/msg-3', undefined)
|
|
61
54
|
})
|
|
62
55
|
})
|
|
63
56
|
})
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import { BaseApi } from '../../core/BaseApi'
|
|
2
|
-
import type { HasGetById } from '../../core/types'
|
|
3
|
-
import type { TranscriptResponse } from './types'
|
|
4
2
|
|
|
5
|
-
export class TranscriptsApi extends BaseApi
|
|
3
|
+
export class TranscriptsApi extends BaseApi {
|
|
6
4
|
private readonly basePath = '/transcripts'
|
|
7
5
|
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Starts the transcription of the audio of a message: marks the message as transcribing and dispatches an async
|
|
8
|
+
* job, which calls the transcription service (AI) and debits the AI credits of the account. Resolves with no body;
|
|
9
|
+
* the result shows on the message. An unknown message answers 500.
|
|
10
|
+
*/
|
|
11
|
+
transcribe(messageId: string, headers?: Record<string, string>): Promise<void> {
|
|
12
|
+
return this.client.get<void>(`${this.basePath}/${messageId}`, headers)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Not a read: it starts the transcription of the message. Use {@link transcribe}.
|
|
17
|
+
*/
|
|
18
|
+
getById(id: string, headers?: Record<string, string>): Promise<void> {
|
|
19
|
+
return this.transcribe(id, headers)
|
|
11
20
|
}
|
|
12
21
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated The route answers no body: `TranscriptsApi.transcribe` (and `getById`) resolve `undefined`. The
|
|
3
|
+
* transcription status shows on the message (`isTranscribing`, `transcribeError`).
|
|
4
|
+
*/
|
|
1
5
|
export type TranscriptResponse = {
|
|
2
6
|
id: string
|
|
3
7
|
isTranscribing: boolean
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { WhatsappBusinessTemplatesApi } from './WhatsappBusinessTemplatesApi'
|
|
3
|
+
import type { ApiClient } from '../../core/ApiClient'
|
|
4
|
+
|
|
5
|
+
// ── Mock client factory ──────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
function createMockClient(): ApiClient {
|
|
8
|
+
return {
|
|
9
|
+
setAccessToken: vi.fn().mockReturnThis(),
|
|
10
|
+
request: vi.fn(),
|
|
11
|
+
get: vi.fn(),
|
|
12
|
+
post: vi.fn(),
|
|
13
|
+
put: vi.fn(),
|
|
14
|
+
patch: vi.fn(),
|
|
15
|
+
delete: vi.fn(),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
describe('WhatsappBusinessTemplatesApi', () => {
|
|
22
|
+
let client: ReturnType<typeof createMockClient>
|
|
23
|
+
let api: WhatsappBusinessTemplatesApi
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
client = createMockClient()
|
|
27
|
+
api = new WhatsappBusinessTemplatesApi(client)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('count calls client.get with /whatsapp-business-templates/count', async () => {
|
|
31
|
+
vi.mocked(client.get).mockResolvedValue({ count: 2 })
|
|
32
|
+
await expect(api.count()).resolves.toEqual({ count: 2 })
|
|
33
|
+
expect(client.get).toHaveBeenCalledWith('/whatsapp-business-templates/count', undefined)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('getHsmLimit calls client.get with /whatsapp-business-templates/:accountId/hsmLimit', async () => {
|
|
37
|
+
const limit = { hsmLimit: 10, hsmUsedLimit: 3, hasLimit: 7 }
|
|
38
|
+
vi.mocked(client.get).mockResolvedValue(limit)
|
|
39
|
+
await expect(api.getHsmLimit('acc-1')).resolves.toEqual(limit)
|
|
40
|
+
expect(client.get).toHaveBeenCalledWith(
|
|
41
|
+
'/whatsapp-business-templates/acc-1/hsmLimit',
|
|
42
|
+
undefined,
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('sendToReview posts to /whatsapp-business-templates/:id/send-to-review', async () => {
|
|
47
|
+
await api.sendToReview('t-1')
|
|
48
|
+
expect(client.post).toHaveBeenCalledWith(
|
|
49
|
+
'/whatsapp-business-templates/t-1/send-to-review',
|
|
50
|
+
undefined,
|
|
51
|
+
undefined,
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('refreshTemplates posts to /whatsapp-business-templates/refresh-templates and resolves OK', async () => {
|
|
56
|
+
vi.mocked(client.post).mockResolvedValue('OK')
|
|
57
|
+
await expect(api.refreshTemplates()).resolves.toBe('OK')
|
|
58
|
+
expect(client.post).toHaveBeenCalledWith(
|
|
59
|
+
'/whatsapp-business-templates/refresh-templates',
|
|
60
|
+
undefined,
|
|
61
|
+
undefined,
|
|
62
|
+
)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('resume calls client.get with /whatsapp-business-templates/resume', async () => {
|
|
66
|
+
vi.mocked(client.get).mockResolvedValue([])
|
|
67
|
+
await expect(api.resume()).resolves.toEqual([])
|
|
68
|
+
expect(client.get).toHaveBeenCalledWith('/whatsapp-business-templates/resume', undefined)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('create posts the draft with status and namespace', async () => {
|
|
72
|
+
const body = {
|
|
73
|
+
name: 'qa_template',
|
|
74
|
+
internalName: 'QA',
|
|
75
|
+
status: '',
|
|
76
|
+
namespace: '',
|
|
77
|
+
components: [{ type: 'BODY' as const, text: 'Olá' }],
|
|
78
|
+
serviceId: 's-1',
|
|
79
|
+
messageType: 'text_only' as const,
|
|
80
|
+
language: 'pt_BR' as const,
|
|
81
|
+
category: 'UTILITY' as const,
|
|
82
|
+
}
|
|
83
|
+
await api.create(body)
|
|
84
|
+
expect(client.post).toHaveBeenCalledWith('/whatsapp-business-templates', body, undefined)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -1,17 +1,91 @@
|
|
|
1
1
|
import type { ApiClient } from '../../core/ApiClient'
|
|
2
|
+
import type { ListQuery } from '../../core/types'
|
|
2
3
|
import { BaseCrudApi } from '../../core/BaseCrudApi'
|
|
3
4
|
import type {
|
|
4
5
|
WhatsappBusinessTemplate,
|
|
5
6
|
CreateWhatsappBusinessTemplatePayload,
|
|
6
7
|
UpdateWhatsappBusinessTemplatePayload,
|
|
8
|
+
WhatsappBusinessTemplateHsmLimit,
|
|
7
9
|
} from './types'
|
|
8
10
|
|
|
11
|
+
/**
|
|
12
|
+
* WhatsApp Business templates. Create, update, reads and count are local; `sendToReview`, `refreshTemplates`,
|
|
13
|
+
* `resume` and the delete of a template of a `whatsapp-business` connection call the provider (Meta, 360Dialog).
|
|
14
|
+
* `getById` answers `null` for an unknown id.
|
|
15
|
+
*/
|
|
9
16
|
export class WhatsappBusinessTemplatesApi extends BaseCrudApi<
|
|
10
17
|
WhatsappBusinessTemplate,
|
|
11
18
|
CreateWhatsappBusinessTemplatePayload,
|
|
12
|
-
UpdateWhatsappBusinessTemplatePayload
|
|
19
|
+
UpdateWhatsappBusinessTemplatePayload,
|
|
20
|
+
ListQuery<WhatsappBusinessTemplate>,
|
|
21
|
+
unknown
|
|
13
22
|
> {
|
|
14
23
|
constructor(client: ApiClient) {
|
|
15
24
|
super(client, '/whatsapp-business-templates')
|
|
16
25
|
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Counts the templates matching the query.
|
|
29
|
+
* @permissions hsm.view
|
|
30
|
+
*/
|
|
31
|
+
count(
|
|
32
|
+
query?: ListQuery<WhatsappBusinessTemplate>,
|
|
33
|
+
headers?: Record<string, string>,
|
|
34
|
+
): Promise<{ count: number }> {
|
|
35
|
+
return this.client.get<{ count: number }>(
|
|
36
|
+
`${this.basePath}/count${this.toQueryString(query)}`,
|
|
37
|
+
headers,
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reads the template limit of the plan of the account. Send the `accountId` of the current user: the route is
|
|
43
|
+
* outside the error handler of the backend, so an unknown id gets no answer (the request waits for the timeout).
|
|
44
|
+
* @permissions hsm.view
|
|
45
|
+
*/
|
|
46
|
+
getHsmLimit(
|
|
47
|
+
accountId: string,
|
|
48
|
+
headers?: Record<string, string>,
|
|
49
|
+
): Promise<WhatsappBusinessTemplateHsmLimit> {
|
|
50
|
+
return this.client.get<WhatsappBusinessTemplateHsmLimit>(
|
|
51
|
+
`${this.basePath}/${accountId}/hsmLimit`,
|
|
52
|
+
headers,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Deletes a template. On a `whatsapp-business` connection it goes to the provider (and answers what it answers);
|
|
58
|
+
* a draft of another connection type is deleted locally (answers the stored template). An unknown id answers 500.
|
|
59
|
+
* @permissions hsm.view OR hsm.destroy
|
|
60
|
+
*/
|
|
61
|
+
override deleteById(id: string, headers?: Record<string, string>): Promise<unknown> {
|
|
62
|
+
return super.deleteById(id, headers)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* SENDS the template to Meta for approval (through the provider of the connection). An unknown or archived
|
|
67
|
+
* template answers 500 before anything is sent.
|
|
68
|
+
* @permissions hsm.view OR hsm.send OR hsm.send.before.expire
|
|
69
|
+
*/
|
|
70
|
+
sendToReview(id: string, headers?: Record<string, string>): Promise<unknown> {
|
|
71
|
+
return this.client.post<unknown>(`${this.basePath}/${id}/send-to-review`, undefined, headers)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Reads the templates of the connected `whatsapp-business` connections from the provider and syncs them (it may
|
|
76
|
+
* archive the local templates the provider no longer has). Resolves the text `'OK'`.
|
|
77
|
+
* @permissions hsm.view
|
|
78
|
+
*/
|
|
79
|
+
refreshTemplates(headers?: Record<string, string>): Promise<'OK'> {
|
|
80
|
+
return this.client.post<'OK'>(`${this.basePath}/refresh-templates`, undefined, headers)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Reads the template balance from 360Dialog. The backend looks for the 360Dialog connection on the whole platform,
|
|
85
|
+
* not only in the account, and calls 360Dialog when it finds one.
|
|
86
|
+
* @permissions hsm.view
|
|
87
|
+
*/
|
|
88
|
+
resume(headers?: Record<string, string>): Promise<Record<string, unknown>[]> {
|
|
89
|
+
return this.client.get<Record<string, unknown>[]>(`${this.basePath}/resume`, headers)
|
|
90
|
+
}
|
|
17
91
|
}
|
|
@@ -10,7 +10,8 @@ export type WhatsappBusinessTemplateHeaderFormat = 'TEXT' | 'IMAGE' | 'VIDEO' |
|
|
|
10
10
|
|
|
11
11
|
export type WhatsappBusinessTemplateButton = {
|
|
12
12
|
type: string
|
|
13
|
-
|
|
13
|
+
/** Overwritten by the backend on `REQUEST_CONTACT_INFO` buttons. */
|
|
14
|
+
text?: string
|
|
14
15
|
url?: string
|
|
15
16
|
params?: string[]
|
|
16
17
|
phone_number?: string
|
|
@@ -44,11 +45,40 @@ export type WhatsappBusinessTemplate = {
|
|
|
44
45
|
createdAt: string
|
|
45
46
|
updatedAt: string
|
|
46
47
|
deletedAt: string | null
|
|
48
|
+
/** Only the listing includes it by default; `getById` answers it with `include: ['fileExample']`. */
|
|
49
|
+
fileExample?: WhatsappBusinessTemplateFileExample | null
|
|
50
|
+
// relationships
|
|
51
|
+
service?: Record<string, unknown>
|
|
52
|
+
account?: Record<string, unknown>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The media example of a template with a media header. */
|
|
56
|
+
export type WhatsappBusinessTemplateFileExample = Record<string, unknown>
|
|
57
|
+
|
|
58
|
+
/** The media example sent on create/update (`base64Url` as a data URL). */
|
|
59
|
+
export type WhatsappBusinessTemplateFileExampleInput = {
|
|
60
|
+
fileName: string
|
|
61
|
+
base64Url: string
|
|
62
|
+
mimetype: string
|
|
47
63
|
}
|
|
48
64
|
|
|
65
|
+
export type WhatsappBusinessTemplateHsmLimit = {
|
|
66
|
+
hsmLimit: number
|
|
67
|
+
hsmUsedLimit: number
|
|
68
|
+
/** The balance left (a number), or `true` when the account has no limit. */
|
|
69
|
+
hasLimit: number | boolean
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Stored locally (nothing goes to Meta until `sendToReview`). `status` is `''` for a draft and `namespace` is `''`
|
|
74
|
+
* until Meta answers — both are required by the database (500 without them).
|
|
75
|
+
*/
|
|
49
76
|
export type CreateWhatsappBusinessTemplatePayload = {
|
|
50
77
|
name: string
|
|
51
78
|
internalName: string
|
|
79
|
+
status: string
|
|
80
|
+
namespace: string
|
|
81
|
+
fileExample?: WhatsappBusinessTemplateFileExampleInput
|
|
52
82
|
components: WhatsappBusinessTemplateComponent[]
|
|
53
83
|
serviceId: string
|
|
54
84
|
messageType: WhatsappBusinessTemplateMessageType
|
|
@@ -56,7 +86,14 @@ export type CreateWhatsappBusinessTemplatePayload = {
|
|
|
56
86
|
category: WhatsappBusinessTemplateCategory
|
|
57
87
|
}
|
|
58
88
|
|
|
89
|
+
/**
|
|
90
|
+
* The backend validates the same 7 fields of the create on the update (`name`, `internalName`, `components`,
|
|
91
|
+
* `serviceId`, `messageType`, `language`, `category`), but ignores `name`.
|
|
92
|
+
*/
|
|
59
93
|
export type UpdateWhatsappBusinessTemplatePayload = {
|
|
94
|
+
status?: string
|
|
95
|
+
namespace?: string
|
|
96
|
+
fileExample?: WhatsappBusinessTemplateFileExampleInput
|
|
60
97
|
name?: string
|
|
61
98
|
internalName?: string
|
|
62
99
|
components?: WhatsappBusinessTemplateComponent[]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expectError, expectType } from 'tsd'
|
|
2
2
|
import type { ApiClient } from './ApiClient'
|
|
3
3
|
import { BaseCrudApi } from './BaseCrudApi'
|
|
4
|
-
import type { Paginated } from './types'
|
|
4
|
+
import type { ListQuery, Paginated } from './types'
|
|
5
5
|
|
|
6
6
|
// ── Test model ──────────────────────────────────────────────────────
|
|
7
7
|
|
|
@@ -40,6 +40,16 @@ expectType<Promise<Item>>(api.create({ name: 'x' }))
|
|
|
40
40
|
expectType<Promise<Item>>(api.updateById('1', {}))
|
|
41
41
|
expectType<Promise<Item>>(api.deleteById('1'))
|
|
42
42
|
|
|
43
|
+
// Routes that do not answer the resource on create/update (TCreateResponse, TUpdateResponse)
|
|
44
|
+
const writes = new BaseCrudApi<Item, CreateItem, UpdateItem, ListQuery<Item>, 'OK', void, Item[]>(
|
|
45
|
+
client,
|
|
46
|
+
'/items',
|
|
47
|
+
)
|
|
48
|
+
expectType<Promise<void>>(writes.create({ name: 'x' }))
|
|
49
|
+
expectType<Promise<Item[]>>(writes.updateById('1', {}))
|
|
50
|
+
expectType<Promise<'OK'>>(writes.deleteById('1'))
|
|
51
|
+
expectType<Promise<Item>>(writes.getById('1'))
|
|
52
|
+
|
|
43
53
|
expectError(api.create({}))
|
|
44
54
|
expectError(api.create({ name: 1 }))
|
|
45
55
|
expectError(api.updateById('1', { name: 1 }))
|
package/src/core/BaseCrudApi.ts
CHANGED
|
@@ -16,7 +16,9 @@ export type {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Generic CRUD resource: the reads of `BaseReadApi` plus `create`, `updateById` and `deleteById`.
|
|
19
|
-
* `TDeleteResponse` is what `deleteById` resolves to (most routes answer the text `'OK'`)
|
|
19
|
+
* `TDeleteResponse` is what `deleteById` resolves to (most routes answer the text `'OK'`);
|
|
20
|
+
* `TCreateResponse` and `TUpdateResponse` are what `create` and `updateById` resolve to, for the routes
|
|
21
|
+
* that do not answer the resource (an empty body, `{}`, another model).
|
|
20
22
|
*/
|
|
21
23
|
export class BaseCrudApi<
|
|
22
24
|
TResponse,
|
|
@@ -24,24 +26,31 @@ export class BaseCrudApi<
|
|
|
24
26
|
TUpdate = TCreate,
|
|
25
27
|
TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,
|
|
26
28
|
TDeleteResponse = TResponse,
|
|
29
|
+
TCreateResponse = TResponse,
|
|
30
|
+
TUpdateResponse = TResponse,
|
|
27
31
|
>
|
|
28
32
|
extends BaseReadApi<TResponse, TQuery>
|
|
29
|
-
implements
|
|
33
|
+
implements
|
|
34
|
+
CrudApi<TResponse, TCreate, TUpdate, TQuery, TDeleteResponse, TCreateResponse, TUpdateResponse>
|
|
30
35
|
{
|
|
31
36
|
/**
|
|
32
37
|
* Creates a new resource.
|
|
33
38
|
* @permissions $resourceName.create
|
|
34
39
|
*/
|
|
35
|
-
create(body: TCreate, headers?: Record<string, string>): Promise<
|
|
36
|
-
return this.client.post<
|
|
40
|
+
create(body: TCreate, headers?: Record<string, string>): Promise<TCreateResponse> {
|
|
41
|
+
return this.client.post<TCreateResponse, TCreate>(this.basePath, body, headers)
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
45
|
* Updates an existing resource by ID.
|
|
41
46
|
* @permissions $resourceName.update
|
|
42
47
|
*/
|
|
43
|
-
updateById(
|
|
44
|
-
|
|
48
|
+
updateById(
|
|
49
|
+
id: string,
|
|
50
|
+
body: TUpdate,
|
|
51
|
+
headers?: Record<string, string>,
|
|
52
|
+
): Promise<TUpdateResponse> {
|
|
53
|
+
return this.client.put<TUpdateResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
/**
|
package/src/core/types.ts
CHANGED
|
@@ -160,8 +160,10 @@ export type CrudApi<
|
|
|
160
160
|
TUpdate = TCreate,
|
|
161
161
|
TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,
|
|
162
162
|
TDeleteResponse = TResponse,
|
|
163
|
+
TCreateResponse = TResponse,
|
|
164
|
+
TUpdateResponse = TResponse,
|
|
163
165
|
> = HasGetMany<TResponse, TQuery> &
|
|
164
166
|
HasGetById<TResponse> &
|
|
165
|
-
HasCreate<
|
|
166
|
-
HasUpdateById<
|
|
167
|
+
HasCreate<TCreateResponse, TCreate> &
|
|
168
|
+
HasUpdateById<TUpdateResponse, TUpdate> &
|
|
167
169
|
HasDeleteById<TDeleteResponse>
|