@ikatec/digisac-api-sdk 4.11.0 → 4.13.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/absence/AbsenceApi.cjs +46 -2
- package/dist/apis/absence/AbsenceApi.cjs.map +1 -1
- package/dist/apis/absence/AbsenceApi.d.ts +38 -4
- package/dist/apis/absence/AbsenceApi.d.ts.map +1 -1
- package/dist/apis/absence/AbsenceApi.mjs +46 -2
- package/dist/apis/absence/AbsenceApi.mjs.map +1 -1
- package/dist/apis/absence/types.d.ts +51 -2
- package/dist/apis/absence/types.d.ts.map +1 -1
- package/dist/apis/activityLog/ActivityLogApi.cjs +10 -1
- package/dist/apis/activityLog/ActivityLogApi.cjs.map +1 -1
- package/dist/apis/activityLog/ActivityLogApi.d.ts +10 -3
- package/dist/apis/activityLog/ActivityLogApi.d.ts.map +1 -1
- package/dist/apis/activityLog/ActivityLogApi.mjs +10 -1
- package/dist/apis/activityLog/ActivityLogApi.mjs.map +1 -1
- package/dist/apis/activityLog/types.d.ts +48 -8
- package/dist/apis/activityLog/types.d.ts.map +1 -1
- package/dist/apis/categories/types.d.ts +1 -0
- package/dist/apis/categories/types.d.ts.map +1 -1
- package/dist/apis/creditMovements/CreditMovementsApi.cjs +31 -2
- package/dist/apis/creditMovements/CreditMovementsApi.cjs.map +1 -1
- package/dist/apis/creditMovements/CreditMovementsApi.d.ts +21 -3
- package/dist/apis/creditMovements/CreditMovementsApi.d.ts.map +1 -1
- package/dist/apis/creditMovements/CreditMovementsApi.mjs +31 -2
- package/dist/apis/creditMovements/CreditMovementsApi.mjs.map +1 -1
- package/dist/apis/creditMovements/types.d.ts +73 -2
- package/dist/apis/creditMovements/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/knowledgeBase/KnowledgeBaseApi.cjs +27 -0
- package/dist/apis/knowledgeBase/KnowledgeBaseApi.cjs.map +1 -1
- package/dist/apis/knowledgeBase/KnowledgeBaseApi.d.ts +22 -1
- package/dist/apis/knowledgeBase/KnowledgeBaseApi.d.ts.map +1 -1
- package/dist/apis/knowledgeBase/KnowledgeBaseApi.mjs +27 -0
- package/dist/apis/knowledgeBase/KnowledgeBaseApi.mjs.map +1 -1
- package/dist/apis/knowledgeBase/types.d.ts +56 -2
- package/dist/apis/knowledgeBase/types.d.ts.map +1 -1
- package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.cjs +26 -0
- package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.cjs.map +1 -1
- package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.d.ts +24 -0
- package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.d.ts.map +1 -1
- package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.mjs +26 -0
- package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.mjs.map +1 -1
- package/dist/apis/knowledgeBaseItem/types.d.ts +23 -6
- package/dist/apis/knowledgeBaseItem/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/notifications/NotificationsApi.cjs +50 -2
- package/dist/apis/notifications/NotificationsApi.cjs.map +1 -1
- package/dist/apis/notifications/NotificationsApi.d.ts +41 -3
- package/dist/apis/notifications/NotificationsApi.d.ts.map +1 -1
- package/dist/apis/notifications/NotificationsApi.mjs +50 -2
- package/dist/apis/notifications/NotificationsApi.mjs.map +1 -1
- package/dist/apis/notifications/types.d.ts +54 -8
- package/dist/apis/notifications/types.d.ts.map +1 -1
- package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs.map +1 -1
- package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts +4 -1
- package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts.map +1 -1
- package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs.map +1 -1
- package/dist/apis/serviceAccessManagement/types.d.ts +3 -0
- package/dist/apis/serviceAccessManagement/types.d.ts.map +1 -1
- package/dist/apis/stickerUsers/StickerUsersApi.cjs.map +1 -1
- package/dist/apis/stickerUsers/StickerUsersApi.d.ts +3 -1
- package/dist/apis/stickerUsers/StickerUsersApi.d.ts.map +1 -1
- package/dist/apis/stickerUsers/StickerUsersApi.mjs.map +1 -1
- package/dist/apis/stickerUsers/types.d.ts +4 -0
- package/dist/apis/stickerUsers/types.d.ts.map +1 -1
- package/dist/apis/stickers/StickersApi.cjs +27 -3
- package/dist/apis/stickers/StickersApi.cjs.map +1 -1
- package/dist/apis/stickers/StickersApi.d.ts +24 -4
- package/dist/apis/stickers/StickersApi.d.ts.map +1 -1
- package/dist/apis/stickers/StickersApi.mjs +27 -3
- package/dist/apis/stickers/StickersApi.mjs.map +1 -1
- package/dist/apis/stickers/types.d.ts +28 -4
- package/dist/apis/stickers/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 +51 -0
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.cjs.map +1 -1
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.d.ts +44 -2
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.d.ts.map +1 -1
- package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.mjs +51 -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/package.json +1 -1
- package/src/apis/absence/AbsenceApi.test.ts +65 -0
- package/src/apis/absence/AbsenceApi.ts +77 -4
- package/src/apis/absence/types.ts +53 -2
- package/src/apis/activityLog/ActivityLogApi.test.ts +44 -0
- package/src/apis/activityLog/ActivityLogApi.ts +22 -3
- package/src/apis/activityLog/types.ts +47 -8
- package/src/apis/categories/types.ts +1 -0
- package/src/apis/creditMovements/CreditMovementsApi.test.ts +88 -0
- package/src/apis/creditMovements/CreditMovementsApi.ts +73 -2
- package/src/apis/creditMovements/types.ts +74 -2
- 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/knowledgeBase/KnowledgeBaseApi.test.ts +54 -0
- package/src/apis/knowledgeBase/KnowledgeBaseApi.ts +61 -1
- package/src/apis/knowledgeBase/types.ts +62 -2
- package/src/apis/knowledgeBaseItem/KnowledgeBaseItemApi.test.ts +57 -0
- package/src/apis/knowledgeBaseItem/KnowledgeBaseItemApi.ts +45 -0
- package/src/apis/knowledgeBaseItem/types.ts +23 -6
- package/src/apis/me/MeApi.test.ts +45 -1
- package/src/apis/me/MeApi.ts +83 -28
- package/src/apis/me/types.ts +75 -4
- package/src/apis/notifications/NotificationsApi.test.ts +74 -0
- package/src/apis/notifications/NotificationsApi.ts +88 -3
- package/src/apis/notifications/types.ts +65 -7
- package/src/apis/payloads.test-d.ts +82 -2
- package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.ts +4 -1
- package/src/apis/serviceAccessManagement/types.ts +3 -0
- package/src/apis/stickerUsers/StickerUsersApi.ts +3 -1
- package/src/apis/stickerUsers/types.ts +4 -0
- package/src/apis/stickers/StickersApi.test.ts +58 -0
- package/src/apis/stickers/StickersApi.ts +46 -4
- package/src/apis/stickers/types.ts +29 -4
- 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 +74 -1
- package/src/apis/whatsappBusinessTemplates/types.ts +38 -1
|
@@ -26,6 +26,7 @@ export type CreditMovementOrigin =
|
|
|
26
26
|
| 'additional'
|
|
27
27
|
| 'agent'
|
|
28
28
|
|
|
29
|
+
/** A movement as the transformer answers it (no `userId`; the relations are never served). */
|
|
29
30
|
export type CreditMovement = {
|
|
30
31
|
id: string
|
|
31
32
|
type: CreditMovementType
|
|
@@ -33,19 +34,90 @@ export type CreditMovement = {
|
|
|
33
34
|
serviceType: CreditMovementServiceType
|
|
34
35
|
origin: CreditMovementOrigin | null
|
|
35
36
|
accountId: string
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
/** `null` on the movements of the account (e.g. the trial SMS credits of a new account). */
|
|
38
|
+
serviceId: string | null
|
|
39
|
+
/** @deprecated Not answered: the transformer of the backend does not send it. */
|
|
40
|
+
userId?: string | null
|
|
38
41
|
campaignId: string | null
|
|
39
42
|
createdAt: string
|
|
40
43
|
updatedAt: string
|
|
41
44
|
// relationships
|
|
45
|
+
/** @deprecated Not served: the include is dropped by the backend. */
|
|
42
46
|
service?: Service
|
|
47
|
+
/** @deprecated Not served: the include is dropped by the backend. */
|
|
43
48
|
user?: User | null
|
|
49
|
+
/** @deprecated Not served: the include is dropped by the backend. */
|
|
44
50
|
campaign?: Campaign | null
|
|
45
51
|
}
|
|
46
52
|
|
|
53
|
+
/** None is served: the backend drops every include of this route. */
|
|
47
54
|
export type CreditMovementRelationships = 'service' | 'user' | 'campaign'
|
|
48
55
|
|
|
56
|
+
/** The balance of each service type (`balances`): the movements in minus the movements out. */
|
|
57
|
+
export type CreditBalances = Record<CreditMovementServiceType, number>
|
|
58
|
+
|
|
59
|
+
/** The totals of a service type in `balancesV2`. */
|
|
60
|
+
export type CreditBalanceTotals = {
|
|
61
|
+
ins: number
|
|
62
|
+
outs: number
|
|
63
|
+
balance: number
|
|
64
|
+
used: number
|
|
65
|
+
contractedCredits?: number
|
|
66
|
+
additionalCredits?: number
|
|
67
|
+
realConsumption?: number
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A service type in `balancesV2`: `all` with the totals, `history` with the rows of the period, and one
|
|
72
|
+
* key per connection name with its totals. The `ins` count the credits of the plan (`plan.ai`) and the
|
|
73
|
+
* additional ones: the trial SMS credits are not counted (they show in `balances`).
|
|
74
|
+
*/
|
|
75
|
+
export type CreditBalanceV2 = {
|
|
76
|
+
all: CreditBalanceTotals
|
|
77
|
+
history?: Record<string, unknown>[]
|
|
78
|
+
[serviceName: string]: unknown
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type CreditBalancesV2Query = {
|
|
82
|
+
from?: string
|
|
83
|
+
to?: string
|
|
84
|
+
serviceId?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Query of `totalServices`: without `filters.from`, `filters.to`, `filters.serviceType` and `page` the route
|
|
89
|
+
* answers 200 with no body.
|
|
90
|
+
*/
|
|
91
|
+
export type CreditTotalServicesQuery = {
|
|
92
|
+
filters: {
|
|
93
|
+
from: string
|
|
94
|
+
to: string
|
|
95
|
+
serviceType: CreditMovementServiceType
|
|
96
|
+
servicesIds?: string[]
|
|
97
|
+
}
|
|
98
|
+
/** From 1 (`0` answers 500 `Invalid param`). Five connections per page. */
|
|
99
|
+
page: number
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The total of credits of each connection in the period, five per page (the counts come as text). */
|
|
103
|
+
export type CreditTotalServices = {
|
|
104
|
+
data: { id: string; name: string; total: string }[]
|
|
105
|
+
total: string
|
|
106
|
+
limit: number
|
|
107
|
+
skip: number
|
|
108
|
+
currentPage: string | number
|
|
109
|
+
lastPage: number
|
|
110
|
+
from: number
|
|
111
|
+
to: number
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Query of `exportXlsx`: the period and the conditions in `where.$and` (required by the backend). */
|
|
115
|
+
export type CreditMovementsExportQuery = {
|
|
116
|
+
from: string
|
|
117
|
+
to: string
|
|
118
|
+
where: { $and: Record<string, unknown>[] }
|
|
119
|
+
}
|
|
120
|
+
|
|
49
121
|
export type CreateCreditMovementPayload = {
|
|
50
122
|
type: CreditMovementType
|
|
51
123
|
serviceType: CreditMovementServiceType
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { InteractiveMessagesApi } from './InteractiveMessagesApi'
|
|
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('InteractiveMessagesApi', () => {
|
|
22
|
+
let client: ReturnType<typeof createMockClient>
|
|
23
|
+
let api: InteractiveMessagesApi
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
client = createMockClient()
|
|
27
|
+
api = new InteractiveMessagesApi(client)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe('count', () => {
|
|
31
|
+
it('calls client.get with /interactive-messages/count when no query is provided', async () => {
|
|
32
|
+
vi.mocked(client.get).mockResolvedValue({ count: 4 })
|
|
33
|
+
await expect(api.count()).resolves.toEqual({ count: 4 })
|
|
34
|
+
expect(client.get).toHaveBeenCalledWith('/interactive-messages/count', undefined)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('serializes the query', async () => {
|
|
38
|
+
vi.mocked(client.get).mockResolvedValue({ count: 1 })
|
|
39
|
+
await api.count({ where: { name: 'QA' } })
|
|
40
|
+
const url = vi.mocked(client.get).mock.calls[0]?.[0] as string
|
|
41
|
+
expect(url).toContain('/interactive-messages/count?')
|
|
42
|
+
expect(url).toContain('where')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('create posts the message to /interactive-messages', async () => {
|
|
47
|
+
const body = {
|
|
48
|
+
name: 'QA',
|
|
49
|
+
interactive: {
|
|
50
|
+
type: 'button' as const,
|
|
51
|
+
body: 'Oi',
|
|
52
|
+
action: { buttons: [{ type: 'reply' as const, reply: { id: '1', title: 'Sim' } }] },
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
vi.mocked(client.post).mockResolvedValue({ id: 'im-1' })
|
|
56
|
+
await api.create(body)
|
|
57
|
+
expect(client.post).toHaveBeenCalledWith('/interactive-messages', body, undefined)
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -18,6 +18,32 @@ export class InteractiveMessagesApi extends BaseCrudApi<
|
|
|
18
18
|
super(client, '/interactive-messages')
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Counts the interactive messages matching the query.
|
|
23
|
+
* @permissions interactive-messages.view OR interactive-messages.management.all.departments
|
|
24
|
+
*/
|
|
25
|
+
count(
|
|
26
|
+
query?: ListQuery<InteractiveMessage>,
|
|
27
|
+
headers?: Record<string, string>,
|
|
28
|
+
): Promise<{ count: number }> {
|
|
29
|
+
return this.client.get<{ count: number }>(
|
|
30
|
+
`${this.basePath}/count${this.toQueryString(query)}`,
|
|
31
|
+
headers,
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates an interactive message. Only `cta_url` is validated by the backend (the other types are stored as
|
|
37
|
+
* sent). The answer has no `departments` nor `file`: read them with `getById` and `include`.
|
|
38
|
+
* @permissions interactive-messages.create OR interactive-messages.management.all.departments
|
|
39
|
+
*/
|
|
40
|
+
override create(
|
|
41
|
+
body: CreateInteractiveMessagePayload,
|
|
42
|
+
headers?: Record<string, string>,
|
|
43
|
+
): Promise<InteractiveMessage> {
|
|
44
|
+
return super.create(body, headers)
|
|
45
|
+
}
|
|
46
|
+
|
|
21
47
|
/**
|
|
22
48
|
* Deletes an interactive message. Resolves the text `'OK'`; an unknown id answers 500.
|
|
23
49
|
* @permissions interactive-messages.destroy OR interactive-messages.management.all.departments
|
|
@@ -169,7 +169,8 @@ type InteractiveMessageFilePayload =
|
|
|
169
169
|
export type InteractiveMessage = {
|
|
170
170
|
id: string
|
|
171
171
|
name: string
|
|
172
|
-
|
|
172
|
+
/** @deprecated Not a field of the backend (no such column): the type is `interactive.type`. */
|
|
173
|
+
type?: 'list' | 'button' | 'cta_url' | 'request_contact_info'
|
|
173
174
|
interactive: InteractiveMessageContent
|
|
174
175
|
archivedAt: string | null
|
|
175
176
|
accountId: string
|
|
@@ -179,9 +180,10 @@ export type InteractiveMessage = {
|
|
|
179
180
|
// relationships
|
|
180
181
|
file?: File | null
|
|
181
182
|
departments?: Department[]
|
|
183
|
+
account?: Record<string, unknown>
|
|
182
184
|
}
|
|
183
185
|
|
|
184
|
-
export type InteractiveMessageRelationships = 'file' | 'departments'
|
|
186
|
+
export type InteractiveMessageRelationships = 'file' | 'departments' | 'account'
|
|
185
187
|
|
|
186
188
|
export type CreateInteractiveMessagePayload = {
|
|
187
189
|
name: string
|
|
@@ -191,6 +193,10 @@ export type CreateInteractiveMessagePayload = {
|
|
|
191
193
|
file?: InteractiveMessageFilePayload
|
|
192
194
|
}
|
|
193
195
|
|
|
196
|
+
/**
|
|
197
|
+
* The backend requires `name` and `interactive` on the update too. It replaces the departments: omitting
|
|
198
|
+
* `departmentIds`/`departments` clears them. Only `file.base64Url` is read (a `file.id` is ignored).
|
|
199
|
+
*/
|
|
194
200
|
export type UpdateInteractiveMessagePayload = {
|
|
195
201
|
name?: string
|
|
196
202
|
interactive?: InteractiveMessageContentInput
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { KnowledgeBaseApi } from './KnowledgeBaseApi'
|
|
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
|
+
requestRaw: vi.fn(),
|
|
12
|
+
get: vi.fn(),
|
|
13
|
+
post: vi.fn(),
|
|
14
|
+
put: vi.fn(),
|
|
15
|
+
patch: vi.fn(),
|
|
16
|
+
delete: vi.fn(),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
describe('KnowledgeBaseApi', () => {
|
|
23
|
+
let client: ReturnType<typeof createMockClient>
|
|
24
|
+
let api: KnowledgeBaseApi
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
client = createMockClient()
|
|
28
|
+
api = new KnowledgeBaseApi(client)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('saveWithItems posts the base with its items', async () => {
|
|
32
|
+
const body = { name: 'QA', items: [], deleteItemIds: [] }
|
|
33
|
+
await api.saveWithItems(body)
|
|
34
|
+
expect(client.post).toHaveBeenCalledWith('/knowledge-base/save-with-items', body, undefined)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('suggestByMessage posts the message to /knowledge-base/suggest-by-message', async () => {
|
|
38
|
+
await api.suggestByMessage({ messageId: 'm-1' })
|
|
39
|
+
expect(client.post).toHaveBeenCalledWith(
|
|
40
|
+
'/knowledge-base/suggest-by-message',
|
|
41
|
+
{ messageId: 'm-1' },
|
|
42
|
+
undefined,
|
|
43
|
+
)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('suggestByTicket posts the ticket to /knowledge-base/suggest-by-ticket', async () => {
|
|
47
|
+
await api.suggestByTicket({ ticketId: 't-1', outsideTheKnowledgeBase: true })
|
|
48
|
+
expect(client.post).toHaveBeenCalledWith(
|
|
49
|
+
'/knowledge-base/suggest-by-ticket',
|
|
50
|
+
{ ticketId: 't-1', outsideTheKnowledgeBase: true },
|
|
51
|
+
undefined,
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import type { ApiClient } from '../../core/ApiClient'
|
|
2
2
|
import type { ListQuery } from '../../core/types'
|
|
3
3
|
import { BaseCrudApi } from '../../core/BaseCrudApi'
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
KnowledgeBase,
|
|
6
|
+
CreateKnowledgeBasePayload,
|
|
7
|
+
UpdateKnowledgeBasePayload,
|
|
8
|
+
KnowledgeBaseSuggestion,
|
|
9
|
+
SaveKnowledgeBaseWithItemsPayload,
|
|
10
|
+
SavedKnowledgeBase,
|
|
11
|
+
SuggestByMessagePayload,
|
|
12
|
+
SuggestByTicketPayload,
|
|
13
|
+
} from './types'
|
|
5
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Knowledge bases of the AI (copilot). The CRUD of a base without items only touches the database; an item
|
|
17
|
+
* VECTORIZES its content in the AI service, and the suggestions call the AI and DEBIT the copilot credits of
|
|
18
|
+
* the account.
|
|
19
|
+
*/
|
|
6
20
|
export class KnowledgeBaseApi extends BaseCrudApi<
|
|
7
21
|
KnowledgeBase,
|
|
8
22
|
CreateKnowledgeBasePayload,
|
|
@@ -22,4 +36,50 @@ export class KnowledgeBaseApi extends BaseCrudApi<
|
|
|
22
36
|
override deleteById(id: string, headers?: Record<string, string>): Promise<false> {
|
|
23
37
|
return super.deleteById(id, headers)
|
|
24
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a base, or updates the one of `id`, with its items, in a transaction. Each item VECTORIZES its
|
|
42
|
+
* content in the AI service (with `items: []` only the database is touched). Resolves the stored base
|
|
43
|
+
* (raw). A body out of the schema answers 500 with the issues of the validation.
|
|
44
|
+
*/
|
|
45
|
+
saveWithItems(
|
|
46
|
+
body: SaveKnowledgeBaseWithItemsPayload,
|
|
47
|
+
headers?: Record<string, string>,
|
|
48
|
+
): Promise<SavedKnowledgeBase> {
|
|
49
|
+
return this.client.post<SavedKnowledgeBase, SaveKnowledgeBaseWithItemsPayload>(
|
|
50
|
+
`${this.basePath}/save-with-items`,
|
|
51
|
+
body,
|
|
52
|
+
headers,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Suggests an answer to a message with the AI (copilot) and DEBITS the copilot credits of the account; an
|
|
58
|
+
* audio is transcribed first (charged too).
|
|
59
|
+
*/
|
|
60
|
+
suggestByMessage(
|
|
61
|
+
body: SuggestByMessagePayload,
|
|
62
|
+
headers?: Record<string, string>,
|
|
63
|
+
): Promise<KnowledgeBaseSuggestion> {
|
|
64
|
+
return this.client.post<KnowledgeBaseSuggestion, SuggestByMessagePayload>(
|
|
65
|
+
`${this.basePath}/suggest-by-message`,
|
|
66
|
+
body,
|
|
67
|
+
headers,
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Suggests an answer to a ticket with the AI (copilot) and DEBITS the copilot credits of the account; the
|
|
73
|
+
* audios of the ticket are transcribed first (charged too).
|
|
74
|
+
*/
|
|
75
|
+
suggestByTicket(
|
|
76
|
+
body: SuggestByTicketPayload,
|
|
77
|
+
headers?: Record<string, string>,
|
|
78
|
+
): Promise<KnowledgeBaseSuggestion> {
|
|
79
|
+
return this.client.post<KnowledgeBaseSuggestion, SuggestByTicketPayload>(
|
|
80
|
+
`${this.basePath}/suggest-by-ticket`,
|
|
81
|
+
body,
|
|
82
|
+
headers,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
25
85
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { KnowledgeBaseItem } from '../knowledgeBaseItem/types'
|
|
2
2
|
|
|
3
|
+
/** A knowledge base as the transformer answers it (`items` always comes: `[]` without the include). */
|
|
3
4
|
export type KnowledgeBase = {
|
|
4
5
|
id: string
|
|
5
6
|
name: string
|
|
@@ -7,19 +8,78 @@ export type KnowledgeBase = {
|
|
|
7
8
|
accountId: string
|
|
8
9
|
createdAt: string
|
|
9
10
|
updatedAt: string
|
|
10
|
-
|
|
11
|
+
/** @deprecated Not answered: the transformer of the backend does not send it. */
|
|
12
|
+
deletedAt?: string | null
|
|
11
13
|
// relationships
|
|
12
14
|
items?: KnowledgeBaseItem[]
|
|
15
|
+
account?: Record<string, unknown>
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
export type KnowledgeBaseRelationships = 'items'
|
|
18
|
+
export type KnowledgeBaseRelationships = 'items' | 'account'
|
|
16
19
|
|
|
17
20
|
export type CreateKnowledgeBasePayload = {
|
|
21
|
+
/** Up to 60 characters. */
|
|
18
22
|
name: string
|
|
23
|
+
/** Up to 600 characters. */
|
|
19
24
|
description?: string
|
|
20
25
|
}
|
|
21
26
|
|
|
27
|
+
/** The backend requires `name` on the update too (400 without it). */
|
|
22
28
|
export type UpdateKnowledgeBasePayload = {
|
|
23
29
|
name?: string
|
|
24
30
|
description?: string | null
|
|
25
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* An item of `saveWithItems`. Any item VECTORIZES its content in the AI service (Haystack) on the save:
|
|
35
|
+
* `text` for `messages`, `url` for `urls` (the service fetches the page), `fileId` for `files`.
|
|
36
|
+
*/
|
|
37
|
+
export type SaveKnowledgeBaseItemInput = {
|
|
38
|
+
name: string
|
|
39
|
+
type: 'urls' | 'files' | 'messages'
|
|
40
|
+
url?: string
|
|
41
|
+
fileId?: string
|
|
42
|
+
text?: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The body of `saveWithItems`: creates the base, or updates the one of `id`, with its items. */
|
|
46
|
+
export type SaveKnowledgeBaseWithItemsPayload = {
|
|
47
|
+
/** 1 to 60 characters. */
|
|
48
|
+
name: string
|
|
49
|
+
/** Up to 600 characters. */
|
|
50
|
+
description?: string
|
|
51
|
+
/** The base to update (absent: a new base). */
|
|
52
|
+
id?: string
|
|
53
|
+
items?: SaveKnowledgeBaseItemInput[]
|
|
54
|
+
/** Items of the base to delete. */
|
|
55
|
+
deleteItemIds?: string[]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The stored base answered by `saveWithItems` (raw: with `synchronized`, without `items`). */
|
|
59
|
+
export type SavedKnowledgeBase = Omit<KnowledgeBase, 'items' | 'account' | 'deletedAt'> & {
|
|
60
|
+
synchronized: boolean | null
|
|
61
|
+
deletedAt: string | null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What the suggestions answer: the suggested text, or a `message` code when nothing was suggested (e.g.
|
|
66
|
+
* `COPILOT_LIMIT_REACHED`, `COPILOT_TRANSCRIPTION_NOT_ENABLED`, `TRANSCRIBING_AUDIO_MESSAGES`).
|
|
67
|
+
*/
|
|
68
|
+
export type KnowledgeBaseSuggestion = {
|
|
69
|
+
message: string
|
|
70
|
+
hasAudioMessage?: boolean
|
|
71
|
+
hadEnoughCreditsToTranscribe?: boolean
|
|
72
|
+
[key: string]: unknown
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type SuggestByMessagePayload = {
|
|
76
|
+
messageId: string
|
|
77
|
+
/** Lets the AI answer beyond the knowledge base. */
|
|
78
|
+
outsideTheKnowledgeBase?: boolean
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type SuggestByTicketPayload = {
|
|
82
|
+
ticketId: string
|
|
83
|
+
/** Lets the AI answer beyond the knowledge base. */
|
|
84
|
+
outsideTheKnowledgeBase?: boolean
|
|
85
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { KnowledgeBaseItemApi } from './KnowledgeBaseItemApi'
|
|
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
|
+
requestRaw: vi.fn(),
|
|
12
|
+
get: vi.fn(),
|
|
13
|
+
post: vi.fn(),
|
|
14
|
+
put: vi.fn(),
|
|
15
|
+
patch: vi.fn(),
|
|
16
|
+
delete: vi.fn(),
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
describe('KnowledgeBaseItemApi', () => {
|
|
23
|
+
let client: ReturnType<typeof createMockClient>
|
|
24
|
+
let api: KnowledgeBaseItemApi
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
client = createMockClient()
|
|
28
|
+
api = new KnowledgeBaseItemApi(client)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('fetchMany posts the ids to /knowledge-base-item/fetch-many', async () => {
|
|
32
|
+
vi.mocked(client.post).mockResolvedValue([])
|
|
33
|
+
await expect(api.fetchMany(['i-1'])).resolves.toEqual([])
|
|
34
|
+
expect(client.post).toHaveBeenCalledWith(
|
|
35
|
+
'/knowledge-base-item/fetch-many',
|
|
36
|
+
{ ids: ['i-1'] },
|
|
37
|
+
undefined,
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('deleteMany sends DELETE /knowledge-base-item/many with the ids in the body', async () => {
|
|
42
|
+
vi.mocked(client.request).mockResolvedValue(true)
|
|
43
|
+
await expect(api.deleteMany(['i-1'])).resolves.toBe(true)
|
|
44
|
+
expect(client.request).toHaveBeenCalledWith(
|
|
45
|
+
'DELETE',
|
|
46
|
+
'/knowledge-base-item/many',
|
|
47
|
+
{ ids: ['i-1'] },
|
|
48
|
+
undefined,
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('checkPdfText posts the file to /knowledge-base-item/check-pdf-text', async () => {
|
|
53
|
+
vi.mocked(client.post).mockResolvedValue({ hasText: false })
|
|
54
|
+
await expect(api.checkPdfText({})).resolves.toEqual({ hasText: false })
|
|
55
|
+
expect(client.post).toHaveBeenCalledWith('/knowledge-base-item/check-pdf-text', {}, undefined)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -7,6 +7,10 @@ import type {
|
|
|
7
7
|
UpdateKnowledgeBaseItemPayload,
|
|
8
8
|
} from './types'
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Items of the knowledge bases. Creating an item VECTORIZES its content in the AI service; reading the
|
|
12
|
+
* `docs` (include, `fetchMany`) reads them from it.
|
|
13
|
+
*/
|
|
10
14
|
export class KnowledgeBaseItemApi extends BaseCrudApi<
|
|
11
15
|
KnowledgeBaseItem,
|
|
12
16
|
CreateKnowledgeBaseItemPayload,
|
|
@@ -26,4 +30,45 @@ export class KnowledgeBaseItemApi extends BaseCrudApi<
|
|
|
26
30
|
override deleteById(id: string, headers?: Record<string, string>): Promise<false> {
|
|
27
31
|
return super.deleteById(id, headers)
|
|
28
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Reads the items of `ids` with their documents (read from the AI service). Resolves the stored items, raw:
|
|
36
|
+
* unlike the other reads they carry `knowledgeBaseId`, `fileId` and `deletedAt`. An empty or absent `ids`
|
|
37
|
+
* answers 400 `Invalid knowledge base item ids`.
|
|
38
|
+
*/
|
|
39
|
+
fetchMany(ids: string[], headers?: Record<string, string>): Promise<KnowledgeBaseItem[]> {
|
|
40
|
+
return this.client.post<KnowledgeBaseItem[], { ids: string[] }>(
|
|
41
|
+
`${this.basePath}/fetch-many`,
|
|
42
|
+
{ ids },
|
|
43
|
+
headers,
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Deletes the items of `ids` of the account (soft delete; their documents stay in the AI service).
|
|
49
|
+
* Resolves `true`; an empty or absent `ids` answers 400 `Invalid knowledge base item ids`.
|
|
50
|
+
*/
|
|
51
|
+
deleteMany(ids: string[], headers?: Record<string, string>): Promise<true> {
|
|
52
|
+
return this.client.request<true, { ids: string[] }>(
|
|
53
|
+
'DELETE',
|
|
54
|
+
`${this.basePath}/many`,
|
|
55
|
+
{ ids },
|
|
56
|
+
headers,
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tells whether the PDF of `fileId` has text to be vectorized (read locally, no AI). Without `fileId` it
|
|
62
|
+
* resolves `{ hasText: false }`.
|
|
63
|
+
*/
|
|
64
|
+
checkPdfText(
|
|
65
|
+
body: { fileId?: string },
|
|
66
|
+
headers?: Record<string, string>,
|
|
67
|
+
): Promise<{ hasText: boolean }> {
|
|
68
|
+
return this.client.post<{ hasText: boolean }, { fileId?: string }>(
|
|
69
|
+
`${this.basePath}/check-pdf-text`,
|
|
70
|
+
body,
|
|
71
|
+
headers,
|
|
72
|
+
)
|
|
73
|
+
}
|
|
29
74
|
}
|
|
@@ -1,35 +1,52 @@
|
|
|
1
1
|
import type { File } from '../files/types'
|
|
2
2
|
import type { KnowledgeBase } from '../knowledgeBase/types'
|
|
3
3
|
|
|
4
|
+
/** An item as the transformer answers it (no `knowledgeBaseId`, `fileId` nor `deletedAt`). */
|
|
4
5
|
export type KnowledgeBaseItem = {
|
|
5
6
|
id: string
|
|
6
7
|
name: string
|
|
7
|
-
type: string
|
|
8
|
+
type: 'urls' | 'files' | 'messages' | (string & {})
|
|
8
9
|
sourceId: string | null
|
|
9
10
|
url: string | null
|
|
10
11
|
accountId: string
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
/** @deprecated Not answered: the transformer of the backend does not send it. */
|
|
13
|
+
knowledgeBaseId?: string
|
|
14
|
+
/** @deprecated Not answered: the transformer of the backend does not send it. */
|
|
15
|
+
fileId?: string | null
|
|
13
16
|
createdAt: string
|
|
14
17
|
updatedAt: string
|
|
15
|
-
|
|
18
|
+
/** @deprecated Not answered: the transformer of the backend does not send it. */
|
|
19
|
+
deletedAt?: string | null
|
|
16
20
|
// relationships
|
|
21
|
+
/** @deprecated Not served: the include is dropped by the backend. */
|
|
17
22
|
knowledgeBase?: KnowledgeBase
|
|
18
23
|
file?: File | null
|
|
24
|
+
/** The vectorized documents (the include reads them from the AI service). */
|
|
25
|
+
docs?: Record<string, unknown>[]
|
|
26
|
+
account?: Record<string, unknown>
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
export type KnowledgeBaseItemRelationships = 'knowledgeBase' | 'file'
|
|
29
|
+
export type KnowledgeBaseItemRelationships = 'knowledgeBase' | 'file' | 'docs' | 'account'
|
|
22
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Creates an item and VECTORIZES its content in the AI service right away: `text` (type `messages`), `url`
|
|
33
|
+
* (type `urls`, the service fetches the page) or `fileId` (type `files`). Without any of the three it
|
|
34
|
+
* answers 400 `You must provide fileId, text, or url`.
|
|
35
|
+
*/
|
|
23
36
|
export type CreateKnowledgeBaseItemPayload = {
|
|
24
37
|
name: string
|
|
25
|
-
type: string
|
|
38
|
+
type: 'urls' | 'files' | 'messages' | (string & {})
|
|
26
39
|
knowledgeBaseId: string
|
|
40
|
+
/** @deprecated Ignored by the backend. */
|
|
27
41
|
sourceId?: string
|
|
42
|
+
text?: string
|
|
28
43
|
url?: string
|
|
29
44
|
fileId?: string
|
|
30
45
|
}
|
|
31
46
|
|
|
47
|
+
/** The backend requires `name`, `type` and `knowledgeBaseId` on the update too (400 without them). */
|
|
32
48
|
export type UpdateKnowledgeBaseItemPayload = {
|
|
49
|
+
knowledgeBaseId?: string
|
|
33
50
|
name?: string
|
|
34
51
|
type?: string
|
|
35
52
|
sourceId?: string | null
|
|
@@ -110,6 +110,50 @@ describe('MeApi', () => {
|
|
|
110
110
|
})
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
+
describe('getMe with a query', () => {
|
|
114
|
+
it('serializes the include and keeps the headers', async () => {
|
|
115
|
+
vi.mocked(client.get).mockResolvedValue(user)
|
|
116
|
+
const headers = { 'X-Custom': 'h' }
|
|
117
|
+
await api.getMe({ include: ['roles', 'account'] }, headers)
|
|
118
|
+
const [url, sent] = vi.mocked(client.get).mock.calls[0] ?? []
|
|
119
|
+
expect(url).toMatch(/^\/me\?/)
|
|
120
|
+
expect(url).toContain('include')
|
|
121
|
+
expect(sent).toBe(headers)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe('getAccount with where.id', () => {
|
|
126
|
+
it('serializes the id of the account', async () => {
|
|
127
|
+
vi.mocked(client.get).mockResolvedValue(account)
|
|
128
|
+
await api.getAccount({ where: { id: 'acc-1' } })
|
|
129
|
+
const url = vi.mocked(client.get).mock.calls[0]?.[0] as string
|
|
130
|
+
expect(url).toMatch(/^\/me\/account\?/)
|
|
131
|
+
expect(url).toContain('acc-1')
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
describe('getAccountAmounts with a filter', () => {
|
|
136
|
+
it('sends the filter as a comma-separated list', async () => {
|
|
137
|
+
vi.mocked(client.get).mockResolvedValue({ accountId: 'acc-1' })
|
|
138
|
+
await api.getAccountAmounts({ filter: ['services.amounts', 'users.amounts'] })
|
|
139
|
+
const url = vi.mocked(client.get).mock.calls[0]?.[0] as string
|
|
140
|
+
expect(url).toMatch(/^\/me\/get-account-amounts\?/)
|
|
141
|
+
expect(decodeURIComponent(url)).toContain('services.amounts,users.amounts')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('keeps the headers-only call', async () => {
|
|
145
|
+
const headers = { 'X-Custom': 'h' }
|
|
146
|
+
await api.getAccountAmounts(headers)
|
|
147
|
+
expect(client.get).toHaveBeenCalledWith('/me/get-account-amounts', headers)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('keeps a header that comes as undefined at runtime as a header', async () => {
|
|
151
|
+
const headers = { Authorization: undefined } as unknown as Record<string, string>
|
|
152
|
+
await api.getAccountAmounts(headers)
|
|
153
|
+
expect(client.get).toHaveBeenCalledWith('/me/get-account-amounts', headers)
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
113
157
|
// ── getAccount ───────────────────────────────────────────────────
|
|
114
158
|
|
|
115
159
|
describe('getAccount', () => {
|
|
@@ -252,7 +296,7 @@ describe('MeApi', () => {
|
|
|
252
296
|
describe('registerToken', () => {
|
|
253
297
|
it('calls client.post with /me/one-signal/register-token and body', async () => {
|
|
254
298
|
vi.mocked(client.post).mockResolvedValue(undefined)
|
|
255
|
-
const body: RegisterTokenPayload = {
|
|
299
|
+
const body: RegisterTokenPayload = { data: { oneSignalToken: 'abc123' } }
|
|
256
300
|
await api.registerToken(body)
|
|
257
301
|
expect(client.post).toHaveBeenCalledWith('/me/one-signal/register-token', body, undefined)
|
|
258
302
|
})
|