@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.
Files changed (142) hide show
  1. package/dist/apis/absence/AbsenceApi.cjs +46 -2
  2. package/dist/apis/absence/AbsenceApi.cjs.map +1 -1
  3. package/dist/apis/absence/AbsenceApi.d.ts +38 -4
  4. package/dist/apis/absence/AbsenceApi.d.ts.map +1 -1
  5. package/dist/apis/absence/AbsenceApi.mjs +46 -2
  6. package/dist/apis/absence/AbsenceApi.mjs.map +1 -1
  7. package/dist/apis/absence/types.d.ts +51 -2
  8. package/dist/apis/absence/types.d.ts.map +1 -1
  9. package/dist/apis/activityLog/ActivityLogApi.cjs +10 -1
  10. package/dist/apis/activityLog/ActivityLogApi.cjs.map +1 -1
  11. package/dist/apis/activityLog/ActivityLogApi.d.ts +10 -3
  12. package/dist/apis/activityLog/ActivityLogApi.d.ts.map +1 -1
  13. package/dist/apis/activityLog/ActivityLogApi.mjs +10 -1
  14. package/dist/apis/activityLog/ActivityLogApi.mjs.map +1 -1
  15. package/dist/apis/activityLog/types.d.ts +48 -8
  16. package/dist/apis/activityLog/types.d.ts.map +1 -1
  17. package/dist/apis/categories/types.d.ts +1 -0
  18. package/dist/apis/categories/types.d.ts.map +1 -1
  19. package/dist/apis/creditMovements/CreditMovementsApi.cjs +31 -2
  20. package/dist/apis/creditMovements/CreditMovementsApi.cjs.map +1 -1
  21. package/dist/apis/creditMovements/CreditMovementsApi.d.ts +21 -3
  22. package/dist/apis/creditMovements/CreditMovementsApi.d.ts.map +1 -1
  23. package/dist/apis/creditMovements/CreditMovementsApi.mjs +31 -2
  24. package/dist/apis/creditMovements/CreditMovementsApi.mjs.map +1 -1
  25. package/dist/apis/creditMovements/types.d.ts +73 -2
  26. package/dist/apis/creditMovements/types.d.ts.map +1 -1
  27. package/dist/apis/interactiveMessages/InteractiveMessagesApi.cjs +15 -0
  28. package/dist/apis/interactiveMessages/InteractiveMessagesApi.cjs.map +1 -1
  29. package/dist/apis/interactiveMessages/InteractiveMessagesApi.d.ts +13 -0
  30. package/dist/apis/interactiveMessages/InteractiveMessagesApi.d.ts.map +1 -1
  31. package/dist/apis/interactiveMessages/InteractiveMessagesApi.mjs +15 -0
  32. package/dist/apis/interactiveMessages/InteractiveMessagesApi.mjs.map +1 -1
  33. package/dist/apis/interactiveMessages/types.d.ts +8 -2
  34. package/dist/apis/interactiveMessages/types.d.ts.map +1 -1
  35. package/dist/apis/knowledgeBase/KnowledgeBaseApi.cjs +27 -0
  36. package/dist/apis/knowledgeBase/KnowledgeBaseApi.cjs.map +1 -1
  37. package/dist/apis/knowledgeBase/KnowledgeBaseApi.d.ts +22 -1
  38. package/dist/apis/knowledgeBase/KnowledgeBaseApi.d.ts.map +1 -1
  39. package/dist/apis/knowledgeBase/KnowledgeBaseApi.mjs +27 -0
  40. package/dist/apis/knowledgeBase/KnowledgeBaseApi.mjs.map +1 -1
  41. package/dist/apis/knowledgeBase/types.d.ts +56 -2
  42. package/dist/apis/knowledgeBase/types.d.ts.map +1 -1
  43. package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.cjs +26 -0
  44. package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.cjs.map +1 -1
  45. package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.d.ts +24 -0
  46. package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.d.ts.map +1 -1
  47. package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.mjs +26 -0
  48. package/dist/apis/knowledgeBaseItem/KnowledgeBaseItemApi.mjs.map +1 -1
  49. package/dist/apis/knowledgeBaseItem/types.d.ts +23 -6
  50. package/dist/apis/knowledgeBaseItem/types.d.ts.map +1 -1
  51. package/dist/apis/me/MeApi.cjs +36 -29
  52. package/dist/apis/me/MeApi.cjs.map +1 -1
  53. package/dist/apis/me/MeApi.d.ts +33 -22
  54. package/dist/apis/me/MeApi.d.ts.map +1 -1
  55. package/dist/apis/me/MeApi.mjs +36 -29
  56. package/dist/apis/me/MeApi.mjs.map +1 -1
  57. package/dist/apis/me/types.d.ts +67 -4
  58. package/dist/apis/me/types.d.ts.map +1 -1
  59. package/dist/apis/notifications/NotificationsApi.cjs +50 -2
  60. package/dist/apis/notifications/NotificationsApi.cjs.map +1 -1
  61. package/dist/apis/notifications/NotificationsApi.d.ts +41 -3
  62. package/dist/apis/notifications/NotificationsApi.d.ts.map +1 -1
  63. package/dist/apis/notifications/NotificationsApi.mjs +50 -2
  64. package/dist/apis/notifications/NotificationsApi.mjs.map +1 -1
  65. package/dist/apis/notifications/types.d.ts +54 -8
  66. package/dist/apis/notifications/types.d.ts.map +1 -1
  67. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs.map +1 -1
  68. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts +4 -1
  69. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts.map +1 -1
  70. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs.map +1 -1
  71. package/dist/apis/serviceAccessManagement/types.d.ts +3 -0
  72. package/dist/apis/serviceAccessManagement/types.d.ts.map +1 -1
  73. package/dist/apis/stickerUsers/StickerUsersApi.cjs.map +1 -1
  74. package/dist/apis/stickerUsers/StickerUsersApi.d.ts +3 -1
  75. package/dist/apis/stickerUsers/StickerUsersApi.d.ts.map +1 -1
  76. package/dist/apis/stickerUsers/StickerUsersApi.mjs.map +1 -1
  77. package/dist/apis/stickerUsers/types.d.ts +4 -0
  78. package/dist/apis/stickerUsers/types.d.ts.map +1 -1
  79. package/dist/apis/stickers/StickersApi.cjs +27 -3
  80. package/dist/apis/stickers/StickersApi.cjs.map +1 -1
  81. package/dist/apis/stickers/StickersApi.d.ts +24 -4
  82. package/dist/apis/stickers/StickersApi.d.ts.map +1 -1
  83. package/dist/apis/stickers/StickersApi.mjs +27 -3
  84. package/dist/apis/stickers/StickersApi.mjs.map +1 -1
  85. package/dist/apis/stickers/types.d.ts +28 -4
  86. package/dist/apis/stickers/types.d.ts.map +1 -1
  87. package/dist/apis/transcripts/TranscriptsApi.cjs +12 -2
  88. package/dist/apis/transcripts/TranscriptsApi.cjs.map +1 -1
  89. package/dist/apis/transcripts/TranscriptsApi.d.ts +11 -5
  90. package/dist/apis/transcripts/TranscriptsApi.d.ts.map +1 -1
  91. package/dist/apis/transcripts/TranscriptsApi.mjs +12 -2
  92. package/dist/apis/transcripts/TranscriptsApi.mjs.map +1 -1
  93. package/dist/apis/transcripts/types.d.ts +4 -0
  94. package/dist/apis/transcripts/types.d.ts.map +1 -1
  95. package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.cjs +51 -0
  96. package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.cjs.map +1 -1
  97. package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.d.ts +44 -2
  98. package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.d.ts.map +1 -1
  99. package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.mjs +51 -0
  100. package/dist/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.mjs.map +1 -1
  101. package/dist/apis/whatsappBusinessTemplates/types.d.ts +34 -1
  102. package/dist/apis/whatsappBusinessTemplates/types.d.ts.map +1 -1
  103. package/package.json +1 -1
  104. package/src/apis/absence/AbsenceApi.test.ts +65 -0
  105. package/src/apis/absence/AbsenceApi.ts +77 -4
  106. package/src/apis/absence/types.ts +53 -2
  107. package/src/apis/activityLog/ActivityLogApi.test.ts +44 -0
  108. package/src/apis/activityLog/ActivityLogApi.ts +22 -3
  109. package/src/apis/activityLog/types.ts +47 -8
  110. package/src/apis/categories/types.ts +1 -0
  111. package/src/apis/creditMovements/CreditMovementsApi.test.ts +88 -0
  112. package/src/apis/creditMovements/CreditMovementsApi.ts +73 -2
  113. package/src/apis/creditMovements/types.ts +74 -2
  114. package/src/apis/interactiveMessages/InteractiveMessagesApi.test.ts +59 -0
  115. package/src/apis/interactiveMessages/InteractiveMessagesApi.ts +26 -0
  116. package/src/apis/interactiveMessages/types.ts +8 -2
  117. package/src/apis/knowledgeBase/KnowledgeBaseApi.test.ts +54 -0
  118. package/src/apis/knowledgeBase/KnowledgeBaseApi.ts +61 -1
  119. package/src/apis/knowledgeBase/types.ts +62 -2
  120. package/src/apis/knowledgeBaseItem/KnowledgeBaseItemApi.test.ts +57 -0
  121. package/src/apis/knowledgeBaseItem/KnowledgeBaseItemApi.ts +45 -0
  122. package/src/apis/knowledgeBaseItem/types.ts +23 -6
  123. package/src/apis/me/MeApi.test.ts +45 -1
  124. package/src/apis/me/MeApi.ts +83 -28
  125. package/src/apis/me/types.ts +75 -4
  126. package/src/apis/notifications/NotificationsApi.test.ts +74 -0
  127. package/src/apis/notifications/NotificationsApi.ts +88 -3
  128. package/src/apis/notifications/types.ts +65 -7
  129. package/src/apis/payloads.test-d.ts +82 -2
  130. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.ts +4 -1
  131. package/src/apis/serviceAccessManagement/types.ts +3 -0
  132. package/src/apis/stickerUsers/StickerUsersApi.ts +3 -1
  133. package/src/apis/stickerUsers/types.ts +4 -0
  134. package/src/apis/stickers/StickersApi.test.ts +58 -0
  135. package/src/apis/stickers/StickersApi.ts +46 -4
  136. package/src/apis/stickers/types.ts +29 -4
  137. package/src/apis/transcripts/TranscriptsApi.test.ts +14 -21
  138. package/src/apis/transcripts/TranscriptsApi.ts +15 -6
  139. package/src/apis/transcripts/types.ts +4 -0
  140. package/src/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.test.ts +86 -0
  141. package/src/apis/whatsappBusinessTemplates/WhatsappBusinessTemplatesApi.ts +74 -1
  142. 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
- serviceId: string
37
- userId: string | null
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
- type: 'list' | 'button' | 'cta_url' | 'request_contact_info'
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 { KnowledgeBase, CreateKnowledgeBasePayload, UpdateKnowledgeBasePayload } from './types'
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
- deletedAt: string | null
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
- knowledgeBaseId: string
12
- fileId: string | null
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
- deletedAt: string | null
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 = { token: 'abc123', platform: 'web' }
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
  })