@ikatec/digisac-api-sdk 4.11.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/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/package.json +1 -1
- package/src/apis/categories/types.ts +1 -0
- 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 +36 -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
|
@@ -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
|
|
@@ -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', () => {
|
package/src/apis/me/MeApi.ts
CHANGED
|
@@ -2,6 +2,11 @@ import { BaseApi } from '../../core/BaseApi'
|
|
|
2
2
|
import type { Account } from '../accounts/types'
|
|
3
3
|
import type { User } from '../users/types'
|
|
4
4
|
import type {
|
|
5
|
+
AccountAmounts,
|
|
6
|
+
AccountAmountsQuery,
|
|
7
|
+
MeAccountQuery,
|
|
8
|
+
MeQuery,
|
|
9
|
+
MeStatusResponse,
|
|
5
10
|
RegisterTokenPayload,
|
|
6
11
|
SetOtpSecretKeyPayload,
|
|
7
12
|
TakeoverPayload,
|
|
@@ -10,30 +15,74 @@ import type {
|
|
|
10
15
|
UpdateMeStatusPayload,
|
|
11
16
|
} from './types'
|
|
12
17
|
|
|
18
|
+
/**
|
|
19
|
+
* A query object always has an array or an object among its values; the headers only have strings (a header that
|
|
20
|
+
* comes as `undefined` at runtime stays a header). That tells the two apart in the methods that took only the
|
|
21
|
+
* headers before and now take a query first.
|
|
22
|
+
*/
|
|
23
|
+
function splitQueryAndHeaders<TQuery extends object>(
|
|
24
|
+
queryOrHeaders: TQuery | Record<string, string> | undefined,
|
|
25
|
+
headers: Record<string, string> | undefined,
|
|
26
|
+
): [TQuery | undefined, Record<string, string> | undefined] {
|
|
27
|
+
const isQuery =
|
|
28
|
+
!!queryOrHeaders &&
|
|
29
|
+
Object.values(queryOrHeaders).some((value) => typeof value === 'object' && value !== null)
|
|
30
|
+
return isQuery
|
|
31
|
+
? [queryOrHeaders as TQuery, headers]
|
|
32
|
+
: [undefined, queryOrHeaders as Record<string, string> | undefined]
|
|
33
|
+
}
|
|
34
|
+
|
|
13
35
|
export class MeApi extends BaseApi {
|
|
14
36
|
/**
|
|
15
|
-
* Returns the currently authenticated user
|
|
37
|
+
* Returns the currently authenticated user. Without `include`, the user comes without relations (roles,
|
|
38
|
+
* departments, timetable, account).
|
|
16
39
|
* @permissions users.view
|
|
17
40
|
*/
|
|
18
|
-
getMe(headers?: Record<string, string>): Promise<User>
|
|
19
|
-
|
|
41
|
+
getMe(headers?: Record<string, string>): Promise<User>
|
|
42
|
+
getMe(query: MeQuery, headers?: Record<string, string>): Promise<User>
|
|
43
|
+
getMe(
|
|
44
|
+
queryOrHeaders?: MeQuery | Record<string, string>,
|
|
45
|
+
headers?: Record<string, string>,
|
|
46
|
+
): Promise<User> {
|
|
47
|
+
const [query, sent] = splitQueryAndHeaders<MeQuery>(queryOrHeaders, headers)
|
|
48
|
+
return this.client.get<User>(`/me${this.toQueryString(query)}`, sent)
|
|
20
49
|
}
|
|
21
50
|
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Returns the account of the current user. Send `where.id` with the `accountId` of the user (what the web app
|
|
53
|
+
* does): without it, the route answers 500.
|
|
54
|
+
*/
|
|
55
|
+
getAccount(headers?: Record<string, string>): Promise<Account>
|
|
56
|
+
getAccount(query: MeAccountQuery, headers?: Record<string, string>): Promise<Account>
|
|
57
|
+
getAccount(
|
|
58
|
+
queryOrHeaders?: MeAccountQuery | Record<string, string>,
|
|
59
|
+
headers?: Record<string, string>,
|
|
60
|
+
): Promise<Account> {
|
|
61
|
+
const [query, sent] = splitQueryAndHeaders<MeAccountQuery>(queryOrHeaders, headers)
|
|
62
|
+
return this.client.get<Account>(`/me/account${this.toQueryString(query)}`, sent)
|
|
25
63
|
}
|
|
26
64
|
|
|
27
65
|
/**
|
|
28
|
-
* Returns
|
|
29
|
-
*
|
|
66
|
+
* Returns the amounts of the current account named in `filter` (services and/or users). The credit amounts are
|
|
67
|
+
* never answered here. An account without credits control answers 400.
|
|
30
68
|
*/
|
|
31
|
-
getAccountAmounts(headers?: Record<string, string>): Promise<
|
|
32
|
-
|
|
69
|
+
getAccountAmounts(headers?: Record<string, string>): Promise<AccountAmounts>
|
|
70
|
+
getAccountAmounts(
|
|
71
|
+
query: AccountAmountsQuery,
|
|
72
|
+
headers?: Record<string, string>,
|
|
73
|
+
): Promise<AccountAmounts>
|
|
74
|
+
getAccountAmounts(
|
|
75
|
+
queryOrHeaders?: AccountAmountsQuery | Record<string, string>,
|
|
76
|
+
headers?: Record<string, string>,
|
|
77
|
+
): Promise<AccountAmounts> {
|
|
78
|
+
const [query, sent] = splitQueryAndHeaders<AccountAmountsQuery>(queryOrHeaders, headers)
|
|
79
|
+
const queryString = query ? this.toQueryString({ filter: query.filter.join(',') }) : ''
|
|
80
|
+
return this.client.get<AccountAmounts>(`/me/get-account-amounts${queryString}`, sent)
|
|
33
81
|
}
|
|
34
82
|
|
|
35
83
|
/**
|
|
36
|
-
* Returns a URL to the Agnus billing/plan management page for the current account.
|
|
84
|
+
* Returns a URL to the Agnus billing/plan management page for the current account. Calls Agnus (an external
|
|
85
|
+
* service) and, for accounts without credits control, reports the consumption to it first.
|
|
37
86
|
* @permissions myAccount.myPlan
|
|
38
87
|
*/
|
|
39
88
|
getAgnusMyPlanUrl(headers?: Record<string, string>): Promise<{ url: string }> {
|
|
@@ -41,39 +90,45 @@ export class MeApi extends BaseApi {
|
|
|
41
90
|
}
|
|
42
91
|
|
|
43
92
|
/**
|
|
44
|
-
* Updates the current user's profile fields such as name, language, and preferences
|
|
45
|
-
*
|
|
46
|
-
* and
|
|
93
|
+
* Updates the current user's profile fields such as name, language, and preferences (`data` and `preferences`
|
|
94
|
+
* replace the stored objects). A new `password` needs `currentPassword` and a recent login, is validated against
|
|
95
|
+
* the last 6 passwords, and revokes the access tokens with an expiration — including the one of this request.
|
|
47
96
|
*/
|
|
48
97
|
updateMe(body: UpdateMePayload, headers?: Record<string, string>): Promise<User> {
|
|
49
98
|
return this.client.put<User, UpdateMePayload>('/me', body, headers)
|
|
50
99
|
}
|
|
51
100
|
|
|
52
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* Updates account-level settings such as name, settings object, wizard progress, and default department. It
|
|
103
|
+
* applies to the whole account; the default department is required while the tickets are enabled.
|
|
104
|
+
*/
|
|
53
105
|
updateAccount(body: UpdateMeAccountPayload, headers?: Record<string, string>): Promise<Account> {
|
|
54
106
|
return this.client.put<Account, UpdateMeAccountPayload>('/me/account', body, headers)
|
|
55
107
|
}
|
|
56
108
|
|
|
57
109
|
/**
|
|
58
|
-
* Updates the presence status of the current user (`online`, `offline`, or `away`)
|
|
110
|
+
* Updates the presence status of the current user (`online`, `offline`, or `away`) for a `client`.
|
|
59
111
|
* If setting to `online`, validates the user's timetable first — if outside working hours,
|
|
60
|
-
* the status is set to `offline` instead.
|
|
112
|
+
* the status is set to `offline` instead. Resolves the status fields of the user.
|
|
61
113
|
*/
|
|
62
|
-
updateStatus(
|
|
63
|
-
|
|
114
|
+
updateStatus(
|
|
115
|
+
body: UpdateMeStatusPayload,
|
|
116
|
+
headers?: Record<string, string>,
|
|
117
|
+
): Promise<MeStatusResponse> {
|
|
118
|
+
return this.client.put<MeStatusResponse, UpdateMeStatusPayload>('/me/status', body, headers)
|
|
64
119
|
}
|
|
65
120
|
|
|
66
121
|
/**
|
|
67
|
-
* Extends the grace period for the current account when it has expired or is about to expire
|
|
68
|
-
* Returns the updated account.
|
|
122
|
+
* Extends the grace period for the current account when it has expired or is about to expire, and notifies the
|
|
123
|
+
* billing webhook. Returns the updated account; an account not in a grace period answers 400.
|
|
69
124
|
*/
|
|
70
125
|
extendAccountGracePeriod(headers?: Record<string, string>): Promise<Account> {
|
|
71
126
|
return this.client.post<Account>('/me/account/extend-grace-period', undefined, headers)
|
|
72
127
|
}
|
|
73
128
|
|
|
74
129
|
/**
|
|
75
|
-
* Registers
|
|
76
|
-
*
|
|
130
|
+
* Registers the push notification tokens of the current user (`data` replaces the whole `data` of the user).
|
|
131
|
+
* Resolves with no body.
|
|
77
132
|
*/
|
|
78
133
|
registerToken(body: RegisterTokenPayload, headers?: Record<string, string>): Promise<void> {
|
|
79
134
|
return this.client.post<void, RegisterTokenPayload>(
|
|
@@ -89,8 +144,8 @@ export class MeApi extends BaseApi {
|
|
|
89
144
|
}
|
|
90
145
|
|
|
91
146
|
/**
|
|
92
|
-
* Sets the current user's status to `online` for the given client and emits a takeover
|
|
93
|
-
* which signals other connected sessions to yield control to this one.
|
|
147
|
+
* Sets the current user's status to `online` for the given client (`'web'` by default) and emits a takeover
|
|
148
|
+
* event, which signals other connected sessions to yield control to this one. Resolves with no body.
|
|
94
149
|
*/
|
|
95
150
|
takeover(body?: TakeoverPayload, headers?: Record<string, string>): Promise<void> {
|
|
96
151
|
return this.client.post<void, TakeoverPayload>('/me/takeover', body, headers)
|
|
@@ -107,9 +162,9 @@ export class MeApi extends BaseApi {
|
|
|
107
162
|
/**
|
|
108
163
|
* Activates OTP (two-factor) authentication for the current user.
|
|
109
164
|
* Requires the `x-digisac-otp` header with a valid TOTP token generated from the secret key
|
|
110
|
-
* previously set via `setOtpSecretKey`.
|
|
165
|
+
* previously set via `setOtpSecretKey`. Resolves the text `'OK'`.
|
|
111
166
|
*/
|
|
112
|
-
activateOtpAuth(headers?: Record<string, string>): Promise<
|
|
113
|
-
return this.client.post<
|
|
167
|
+
activateOtpAuth(headers?: Record<string, string>): Promise<'OK'> {
|
|
168
|
+
return this.client.post<'OK'>('/me/activate-otp-auth', undefined, headers)
|
|
114
169
|
}
|
|
115
170
|
}
|
package/src/apis/me/types.ts
CHANGED
|
@@ -2,9 +2,68 @@ import type { AccountSettings } from '../accounts/types'
|
|
|
2
2
|
import type { UserPreferences } from '../users/types'
|
|
3
3
|
import type { UserStatus } from '../users/types'
|
|
4
4
|
|
|
5
|
+
/** The client of the session that sets the status: the web app or the mobile app. */
|
|
6
|
+
export type MeClient = 'web' | 'app'
|
|
7
|
+
|
|
8
|
+
/** Query of `GET /me`: without `include`, the user comes without relations. */
|
|
9
|
+
export type MeQuery = {
|
|
10
|
+
include?: (
|
|
11
|
+
| 'roles'
|
|
12
|
+
| 'departments'
|
|
13
|
+
| 'timetable'
|
|
14
|
+
| 'account'
|
|
15
|
+
| 'permissions'
|
|
16
|
+
| 'organizations'
|
|
17
|
+
| (string & {})
|
|
18
|
+
)[]
|
|
19
|
+
attributes?: string[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Query of `GET /me/account`: `where.id` is the id of the account of the user (`accountId` of `getMe`). */
|
|
23
|
+
export type MeAccountQuery = {
|
|
24
|
+
where: { id: string }
|
|
25
|
+
include?: string[]
|
|
26
|
+
attributes?: string[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type AccountAmountsFilter = 'services.amounts' | 'users.amounts'
|
|
30
|
+
|
|
31
|
+
/** Query of `GET /me/get-account-amounts`: which amounts to answer (the credit amounts are never answered). */
|
|
32
|
+
export type AccountAmountsQuery = {
|
|
33
|
+
filter: AccountAmountsFilter[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type UsersAmounts = {
|
|
37
|
+
planBase: number
|
|
38
|
+
additional: number
|
|
39
|
+
totalContracted: number
|
|
40
|
+
planBaseConsumed: number
|
|
41
|
+
additionalConsumed: number
|
|
42
|
+
totalConsumed: number
|
|
43
|
+
available: number
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type AccountAmounts = {
|
|
47
|
+
accountId: string
|
|
48
|
+
servicesAmounts?: Record<string, unknown>
|
|
49
|
+
usersAmounts?: UsersAmounts
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** What `updateStatus` answers: the status fields of the user, not the whole user. */
|
|
53
|
+
export type MeStatusResponse = {
|
|
54
|
+
id: string
|
|
55
|
+
updatedAt: string
|
|
56
|
+
status: UserStatus
|
|
57
|
+
clientsStatus: Partial<Record<MeClient, UserStatus>>
|
|
58
|
+
language: string
|
|
59
|
+
account?: unknown
|
|
60
|
+
}
|
|
61
|
+
|
|
5
62
|
export type UpdateMePayload = {
|
|
6
63
|
name?: string
|
|
7
64
|
password?: string
|
|
65
|
+
/** Required with `password`: the current password of the user. */
|
|
66
|
+
currentPassword?: string
|
|
8
67
|
language?: string
|
|
9
68
|
preferences?: UserPreferences
|
|
10
69
|
otpAuthActive?: boolean
|
|
@@ -14,22 +73,34 @@ export type UpdateMePayload = {
|
|
|
14
73
|
|
|
15
74
|
export type UpdateMeStatusPayload = {
|
|
16
75
|
status: UserStatus
|
|
17
|
-
|
|
76
|
+
/** Required by the backend (400 without it). */
|
|
77
|
+
client?: MeClient
|
|
18
78
|
}
|
|
19
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Merged over the account. The default department is required while the tickets are enabled
|
|
82
|
+
* (`settings.ticketsEnabled`; 400 `DEFAULT_DEPARTMENT_REQUIRED`).
|
|
83
|
+
*/
|
|
20
84
|
export type UpdateMeAccountPayload = {
|
|
21
85
|
name?: string
|
|
22
86
|
settings?: Partial<AccountSettings>
|
|
23
|
-
wizardProgress?: string
|
|
87
|
+
wizardProgress?: 'notStarted' | 'started' | 'finished' | (string & {})
|
|
24
88
|
defaultDepartmentId?: string
|
|
89
|
+
defaultDepartment?: { id: string }
|
|
25
90
|
}
|
|
26
91
|
|
|
27
92
|
export type TakeoverPayload = {
|
|
28
|
-
|
|
93
|
+
/** Defaults to `'web'`. */
|
|
94
|
+
client?: MeClient
|
|
29
95
|
}
|
|
30
96
|
|
|
31
97
|
export type SetOtpSecretKeyPayload = {
|
|
32
98
|
secretKey: string
|
|
33
99
|
}
|
|
34
100
|
|
|
35
|
-
|
|
101
|
+
/** The push tokens of the user. `data` REPLACES the whole `data` of the user (it is not merged). */
|
|
102
|
+
export type RegisterTokenPayload = {
|
|
103
|
+
data?: { oneSignalToken?: string; expoPushToken?: string | null; fromApp?: boolean }
|
|
104
|
+
/** The backend ignores the call when it is `true`. */
|
|
105
|
+
isClientUser?: boolean
|
|
106
|
+
}
|
|
@@ -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,
|
|
@@ -70,7 +74,7 @@ import { NotificationsApi } from './notifications/NotificationsApi'
|
|
|
70
74
|
import { OrganizationsApi } from './organizations/OrganizationsApi'
|
|
71
75
|
import { PeopleApi } from './people/PeopleApi'
|
|
72
76
|
import { ServicesApi } from './services/ServicesApi'
|
|
73
|
-
import type { UpdateUserPayload } from './users/types'
|
|
77
|
+
import type { UpdateUserPayload, User } from './users/types'
|
|
74
78
|
import { WebhooksApi } from './webhooks/WebhooksApi'
|
|
75
79
|
|
|
76
80
|
declare const client: ApiClient
|
|
@@ -210,7 +214,10 @@ expectError(
|
|
|
210
214
|
)
|
|
211
215
|
|
|
212
216
|
declare const interactiveMessage: InteractiveMessage
|
|
213
|
-
|
|
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
|
+
)
|
|
214
221
|
if (interactiveMessage.interactive.type === 'cta_url') {
|
|
215
222
|
expectType<CtaUrlInteractiveMessageContent>(interactiveMessage.interactive)
|
|
216
223
|
expectType<string>(interactiveMessage.interactive.action.parameters.url)
|
|
@@ -548,6 +555,33 @@ expectType<Promise<boolean>>(new RolesApi(client).deleteById('r-1'))
|
|
|
548
555
|
expectType<Promise<void>>(new AcceptanceTermsApi(client).deleteById('t-1'))
|
|
549
556
|
expectType<Promise<void>>(new DistributionApi(client).deleteById('d-1'))
|
|
550
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
|
+
|
|
551
585
|
// ── create/updateById: what each route really answers (QARC-1008) ─────
|
|
552
586
|
|
|
553
587
|
expectType<Promise<Distribution | undefined>>(
|
|
@@ -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
|
+
})
|