@ikatec/digisac-api-sdk 4.10.0 → 4.11.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 (57) hide show
  1. package/dist/apis/contactBlockLists/ContactBlockListsApi.cjs +28 -0
  2. package/dist/apis/contactBlockLists/ContactBlockListsApi.cjs.map +1 -1
  3. package/dist/apis/contactBlockLists/ContactBlockListsApi.d.ts +24 -0
  4. package/dist/apis/contactBlockLists/ContactBlockListsApi.d.ts.map +1 -1
  5. package/dist/apis/contactBlockLists/ContactBlockListsApi.mjs +28 -0
  6. package/dist/apis/contactBlockLists/ContactBlockListsApi.mjs.map +1 -1
  7. package/dist/apis/contactBlockLists/types.d.ts +6 -1
  8. package/dist/apis/contactBlockLists/types.d.ts.map +1 -1
  9. package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.cjs +31 -1
  10. package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.cjs.map +1 -1
  11. package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.d.ts +28 -2
  12. package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.d.ts.map +1 -1
  13. package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.mjs +31 -1
  14. package/dist/apis/contactBlockListsControls/ContactBlockListsControlsApi.mjs.map +1 -1
  15. package/dist/apis/contactBlockListsControls/types.d.ts +7 -0
  16. package/dist/apis/contactBlockListsControls/types.d.ts.map +1 -1
  17. package/dist/apis/distribution/DistributionApi.cjs +25 -0
  18. package/dist/apis/distribution/DistributionApi.cjs.map +1 -1
  19. package/dist/apis/distribution/DistributionApi.d.ts +20 -1
  20. package/dist/apis/distribution/DistributionApi.d.ts.map +1 -1
  21. package/dist/apis/distribution/DistributionApi.mjs +25 -0
  22. package/dist/apis/distribution/DistributionApi.mjs.map +1 -1
  23. package/dist/apis/distribution/types.d.ts +18 -1
  24. package/dist/apis/distribution/types.d.ts.map +1 -1
  25. package/dist/apis/integrations/IntegrationsApi.cjs +17 -0
  26. package/dist/apis/integrations/IntegrationsApi.cjs.map +1 -1
  27. package/dist/apis/integrations/IntegrationsApi.d.ts +15 -2
  28. package/dist/apis/integrations/IntegrationsApi.d.ts.map +1 -1
  29. package/dist/apis/integrations/IntegrationsApi.mjs +17 -0
  30. package/dist/apis/integrations/IntegrationsApi.mjs.map +1 -1
  31. package/dist/apis/integrations/types.d.ts +15 -6
  32. package/dist/apis/integrations/types.d.ts.map +1 -1
  33. package/dist/core/BaseCrudApi.cjs +3 -1
  34. package/dist/core/BaseCrudApi.cjs.map +1 -1
  35. package/dist/core/BaseCrudApi.d.ts +6 -4
  36. package/dist/core/BaseCrudApi.d.ts.map +1 -1
  37. package/dist/core/BaseCrudApi.mjs +3 -1
  38. package/dist/core/BaseCrudApi.mjs.map +1 -1
  39. package/dist/core/types.d.ts +1 -1
  40. package/dist/core/types.d.ts.map +1 -1
  41. package/package.json +1 -1
  42. package/src/apis/contactBlockLists/ContactBlockListsApi.test.ts +57 -0
  43. package/src/apis/contactBlockLists/ContactBlockListsApi.ts +44 -0
  44. package/src/apis/contactBlockLists/types.ts +6 -1
  45. package/src/apis/contactBlockListsControls/ContactBlockListsControlsApi.test.ts +48 -0
  46. package/src/apis/contactBlockListsControls/ContactBlockListsControlsApi.ts +49 -2
  47. package/src/apis/contactBlockListsControls/types.ts +7 -0
  48. package/src/apis/distribution/DistributionApi.test.ts +65 -0
  49. package/src/apis/distribution/DistributionApi.ts +39 -1
  50. package/src/apis/distribution/types.ts +17 -1
  51. package/src/apis/integrations/IntegrationsApi.test.ts +42 -0
  52. package/src/apis/integrations/IntegrationsApi.ts +35 -2
  53. package/src/apis/integrations/types.ts +25 -6
  54. package/src/apis/payloads.test-d.ts +58 -0
  55. package/src/core/BaseCrudApi.test-d.ts +11 -1
  56. package/src/core/BaseCrudApi.ts +15 -6
  57. package/src/core/types.ts +4 -2
@@ -1,19 +1,57 @@
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 { Department } from '../departments/types'
4
5
  import type { Distribution, CreateDistributionPayload, UpdateDistributionPayload } from './types'
5
6
 
7
+ /**
8
+ * Ticket distribution rules. `getById` answers `null` for an unknown id and always includes `departments` and
9
+ * `roles` (the query is ignored).
10
+ */
6
11
  export class DistributionApi extends BaseCrudApi<
7
12
  Distribution,
8
13
  CreateDistributionPayload,
9
14
  UpdateDistributionPayload,
10
15
  ListQuery<Distribution>,
11
- void
16
+ void,
17
+ Distribution | undefined,
18
+ Distribution | Department[]
12
19
  > {
13
20
  constructor(client: ApiClient) {
14
21
  super(client, '/distribution')
15
22
  }
16
23
 
24
+ /**
25
+ * Creates a distribution. Resolves the distribution (without `departments`/`roles`), or no body
26
+ * (`undefined`) when `departments` is sent — find it by name with `getMany` in that case.
27
+ * @permissions distribution.create
28
+ */
29
+ override create(
30
+ body: CreateDistributionPayload,
31
+ headers?: Record<string, string>,
32
+ ): Promise<Distribution | undefined> {
33
+ return super.create(body, headers)
34
+ }
35
+
36
+ /**
37
+ * Updates a distribution. The backend reads the id from the body, so it is sent there too. It replaces
38
+ * the links: omitting `roles` or `departments` clears them. Resolves the stored distribution (with
39
+ * `deletedAt`, without the links) or, when `departments` is sent, the updated departments. An unknown id
40
+ * answers 500.
41
+ * @permissions distribution.view OR distribution.update
42
+ */
43
+ override updateById(
44
+ id: string,
45
+ body: UpdateDistributionPayload,
46
+ headers?: Record<string, string>,
47
+ ): Promise<Distribution | Department[]> {
48
+ return this.client.put<Distribution | Department[], UpdateDistributionPayload & { id: string }>(
49
+ `${this.basePath}/${id}`,
50
+ { ...body, id },
51
+ headers,
52
+ )
53
+ }
54
+
17
55
  /**
18
56
  * Deletes a distribution. Resolves with no body, also for an unknown id (200).
19
57
  * @permissions distribution.view OR distribution.destroy
@@ -11,7 +11,8 @@ export type Distribution = {
11
11
  distributeQueue: boolean
12
12
  accountId: string
13
13
  archivedAt: string | null
14
- deletedAt: string | null
14
+ /** Only in the answer of `updateById` (the stored model, not transformed). */
15
+ deletedAt?: string | null
15
16
  createdAt: string
16
17
  updatedAt: string
17
18
  // relationships
@@ -21,6 +22,9 @@ export type Distribution = {
21
22
 
22
23
  export type DistributionRelationships = 'departments' | 'roles'
23
24
 
25
+ /** A department or a role linked to a distribution (only the id is read). */
26
+ export type DistributionLink = { id: string }
27
+
24
28
  export type CreateDistributionPayload = {
25
29
  name: string
26
30
  maxNum: number
@@ -28,8 +32,17 @@ export type CreateDistributionPayload = {
28
32
  timeToRedistribute?: number
29
33
  redistributeAssignedTickets?: boolean
30
34
  distributeQueue?: boolean
35
+ /** An archived distribution is skipped by the distribution crons. */
36
+ archivedAt?: string | null
37
+ /** Departments to distribute; a department has at most one distribution. */
38
+ departments?: DistributionLink[]
39
+ roles?: DistributionLink[]
31
40
  }
32
41
 
42
+ /**
43
+ * The update replaces the links: send `departments` and `roles` again to keep them — omitting them
44
+ * clears them.
45
+ */
33
46
  export type UpdateDistributionPayload = {
34
47
  name?: string
35
48
  maxNum?: number
@@ -37,4 +50,7 @@ export type UpdateDistributionPayload = {
37
50
  timeToRedistribute?: number
38
51
  redistributeAssignedTickets?: boolean
39
52
  distributeQueue?: boolean
53
+ archivedAt?: string | null
54
+ departments?: DistributionLink[]
55
+ roles?: DistributionLink[]
40
56
  }
@@ -0,0 +1,42 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { IntegrationsApi } from './IntegrationsApi'
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('IntegrationsApi', () => {
22
+ let client: ReturnType<typeof createMockClient>
23
+ let api: IntegrationsApi
24
+
25
+ beforeEach(() => {
26
+ client = createMockClient()
27
+ api = new IntegrationsApi(client)
28
+ })
29
+
30
+ it('create posts to /integrations and resolves the empty object the route answers', async () => {
31
+ vi.mocked(client.post).mockResolvedValue({})
32
+ const body = { type: 'menu_button', url: 'https://example.com', icon: '', text: 'QA' }
33
+ await expect(api.create(body)).resolves.toEqual({})
34
+ expect(client.post).toHaveBeenCalledWith('/integrations', body, undefined)
35
+ })
36
+
37
+ it('updateById puts to /integrations/:id and resolves the empty object', async () => {
38
+ vi.mocked(client.put).mockResolvedValue({})
39
+ await expect(api.updateById('int-1', { text: 'QA 2' })).resolves.toEqual({})
40
+ expect(client.put).toHaveBeenCalledWith('/integrations/int-1', { text: 'QA 2' }, undefined)
41
+ })
42
+ })
@@ -1,19 +1,52 @@
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 { Integration, CreateIntegrationPayload, UpdateIntegrationPayload } from './types'
4
+ import type {
5
+ Integration,
6
+ CreateIntegrationPayload,
7
+ UpdateIntegrationPayload,
8
+ IntegrationWriteResponse,
9
+ } from './types'
5
10
 
11
+ /** Integrations of the account (buttons and events of the UI). `getById` answers `null` for an unknown id. */
6
12
  export class IntegrationsApi extends BaseCrudApi<
7
13
  Integration,
8
14
  CreateIntegrationPayload,
9
15
  UpdateIntegrationPayload,
10
16
  ListQuery<Integration>,
11
- 'OK'
17
+ 'OK',
18
+ IntegrationWriteResponse,
19
+ IntegrationWriteResponse
12
20
  > {
13
21
  constructor(client: ApiClient) {
14
22
  super(client, '/integrations')
15
23
  }
16
24
 
25
+ /**
26
+ * Creates an integration. Resolves `{}` — also when the type is unique (`event_ticket_transfer`,
27
+ * `event_ticket_closed`), already exists in the account and nothing is created. Find the integration with
28
+ * `getMany` (e.g. by `text`).
29
+ * @permissions integrations.create
30
+ */
31
+ override create(
32
+ body: CreateIntegrationPayload,
33
+ headers?: Record<string, string>,
34
+ ): Promise<IntegrationWriteResponse> {
35
+ return super.create(body, headers)
36
+ }
37
+
38
+ /**
39
+ * Updates an integration. Resolves `{}`; read it again with `getById`. An unknown id answers 500.
40
+ * @permissions integrations.view OR integrations.update
41
+ */
42
+ override updateById(
43
+ id: string,
44
+ body: UpdateIntegrationPayload,
45
+ headers?: Record<string, string>,
46
+ ): Promise<IntegrationWriteResponse> {
47
+ return super.updateById(id, body, headers)
48
+ }
49
+
17
50
  /**
18
51
  * Deletes an integration. Resolves the text `'OK'`; an unknown id answers 500.
19
52
  * @permissions integrations.view OR integrations.destroy
@@ -1,27 +1,46 @@
1
+ /**
2
+ * The types the UI shows: buttons (`menu_button` in the header, `context_menu_button_*` in the contact and
3
+ * message menus) and events that open the integration when a ticket is transferred or closed (unique per
4
+ * account). The backend accepts any string.
5
+ */
6
+ export type IntegrationType =
7
+ | 'menu_button'
8
+ | 'context_menu_button_contact_modal'
9
+ | 'context_menu_button_contact_side_list'
10
+ | 'context_menu_button_message'
11
+ | 'event_ticket_transfer'
12
+ | 'event_ticket_closed'
13
+ | (string & {})
14
+
15
+ export type IntegrationSize = 'sm' | 'md' | 'lg' | (string & {})
16
+
1
17
  export type Integration = {
2
18
  id: string
3
- type: string
19
+ type: IntegrationType
4
20
  url: string | null
5
21
  icon: string | null
6
22
  text: string | null
7
- size: string | null
23
+ size: IntegrationSize | null
8
24
  accountId: string
9
25
  createdAt: string
10
26
  updatedAt: string
11
27
  }
12
28
 
29
+ /** What `create` and `updateById` resolve to: an empty object. */
30
+ export type IntegrationWriteResponse = Record<string, never>
31
+
13
32
  export type CreateIntegrationPayload = {
14
- type: string
33
+ type: IntegrationType
15
34
  url?: string
16
35
  icon?: string
17
36
  text?: string
18
- size?: string
37
+ size?: IntegrationSize
19
38
  }
20
39
 
21
40
  export type UpdateIntegrationPayload = {
22
- type?: string
41
+ type?: IntegrationType
23
42
  url?: string | null
24
43
  icon?: string | null
25
44
  text?: string | null
26
- size?: string | null
45
+ size?: IntegrationSize | null
27
46
  }
@@ -54,6 +54,8 @@ import type { SummarizeTicketResult } from './contacts/types'
54
54
  import { CustomFieldsApi } from './customFields/CustomFieldsApi'
55
55
  import { DepartmentsApi } from './departments/DepartmentsApi'
56
56
  import type { Department } from './departments/types'
57
+ import type { Distribution } from './distribution/types'
58
+ import type { ContactBlockList } from './contactBlockLists/types'
57
59
  import { DistributionApi } from './distribution/DistributionApi'
58
60
  import { FilesApi } from './files/FilesApi'
59
61
  import type { CreateToUploadResult, DeleteFileResult, File } from './files/types'
@@ -545,6 +547,62 @@ expectType<Promise<'OK'>>(new ContactBlockListsControlsApi(client).deleteById('c
545
547
  expectType<Promise<boolean>>(new RolesApi(client).deleteById('r-1'))
546
548
  expectType<Promise<void>>(new AcceptanceTermsApi(client).deleteById('t-1'))
547
549
  expectType<Promise<void>>(new DistributionApi(client).deleteById('d-1'))
550
+
551
+ // ── create/updateById: what each route really answers (QARC-1008) ─────
552
+
553
+ expectType<Promise<Distribution | undefined>>(
554
+ new DistributionApi(client).create({
555
+ name: 'd',
556
+ maxNum: 1,
557
+ redistributeAll: false,
558
+ departments: [{ id: 'dep-1' }],
559
+ }),
560
+ )
561
+ expectType<Promise<Distribution | Department[]>>(
562
+ new DistributionApi(client).updateById('d-1', {
563
+ name: 'd',
564
+ roles: [{ id: 'r-1' }],
565
+ departments: [],
566
+ }),
567
+ )
568
+ expectType<Promise<Record<string, never>>>(
569
+ new IntegrationsApi(client).create({ type: 'menu_button', text: 'x' }),
570
+ )
571
+ expectType<Promise<Record<string, never>>>(
572
+ new IntegrationsApi(client).updateById('i-1', { text: 'x' }),
573
+ )
574
+ expectType<Promise<void>>(
575
+ new ContactBlockListsControlsApi(client).create({
576
+ reason: 'r',
577
+ servicesIds: ['s-1'],
578
+ contactBlockListsIds: ['l-1'],
579
+ action: 'block',
580
+ revertExceptions: false,
581
+ }),
582
+ )
583
+ expectType<Promise<{ count: number }>>(
584
+ new ContactBlockListsApi(client).count({ where: { name: 'x' } }),
585
+ )
586
+ expectType<Promise<{ count: number }>>(new ContactBlockListsControlsApi(client).count())
587
+ expectType<Promise<Paginated<ContactBlockList>>>(
588
+ new ContactBlockListsApi(client).getMany({ include: ['importFile'] }),
589
+ )
590
+ expectError(
591
+ new DistributionApi(client).create({
592
+ name: 'd',
593
+ maxNum: 1,
594
+ redistributeAll: false,
595
+ departments: ['dep-1'],
596
+ }),
597
+ )
598
+ expectError(
599
+ new ContactBlockListsControlsApi(client).create({
600
+ reason: 'r',
601
+ servicesIds: [],
602
+ contactBlockListsIds: [],
603
+ action: 'remove',
604
+ }),
605
+ )
548
606
  expectType<Promise<false>>(new KnowledgeBaseApi(client).deleteById('k-1'))
549
607
  expectType<Promise<false>>(new KnowledgeBaseItemApi(client).deleteById('k-1'))
550
608
  expectType<Promise<DeleteFileResult>>(new FilesApi(client).deleteById('f-1'))
@@ -1,7 +1,7 @@
1
1
  import { expectError, expectType } from 'tsd'
2
2
  import type { ApiClient } from './ApiClient'
3
3
  import { BaseCrudApi } from './BaseCrudApi'
4
- import type { Paginated } from './types'
4
+ import type { ListQuery, Paginated } from './types'
5
5
 
6
6
  // ── Test model ──────────────────────────────────────────────────────
7
7
 
@@ -40,6 +40,16 @@ expectType<Promise<Item>>(api.create({ name: 'x' }))
40
40
  expectType<Promise<Item>>(api.updateById('1', {}))
41
41
  expectType<Promise<Item>>(api.deleteById('1'))
42
42
 
43
+ // Routes that do not answer the resource on create/update (TCreateResponse, TUpdateResponse)
44
+ const writes = new BaseCrudApi<Item, CreateItem, UpdateItem, ListQuery<Item>, 'OK', void, Item[]>(
45
+ client,
46
+ '/items',
47
+ )
48
+ expectType<Promise<void>>(writes.create({ name: 'x' }))
49
+ expectType<Promise<Item[]>>(writes.updateById('1', {}))
50
+ expectType<Promise<'OK'>>(writes.deleteById('1'))
51
+ expectType<Promise<Item>>(writes.getById('1'))
52
+
43
53
  expectError(api.create({}))
44
54
  expectError(api.create({ name: 1 }))
45
55
  expectError(api.updateById('1', { name: 1 }))
@@ -16,7 +16,9 @@ export type {
16
16
 
17
17
  /**
18
18
  * Generic CRUD resource: the reads of `BaseReadApi` plus `create`, `updateById` and `deleteById`.
19
- * `TDeleteResponse` is what `deleteById` resolves to (most routes answer the text `'OK'`).
19
+ * `TDeleteResponse` is what `deleteById` resolves to (most routes answer the text `'OK'`);
20
+ * `TCreateResponse` and `TUpdateResponse` are what `create` and `updateById` resolve to, for the routes
21
+ * that do not answer the resource (an empty body, `{}`, another model).
20
22
  */
21
23
  export class BaseCrudApi<
22
24
  TResponse,
@@ -24,24 +26,31 @@ export class BaseCrudApi<
24
26
  TUpdate = TCreate,
25
27
  TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,
26
28
  TDeleteResponse = TResponse,
29
+ TCreateResponse = TResponse,
30
+ TUpdateResponse = TResponse,
27
31
  >
28
32
  extends BaseReadApi<TResponse, TQuery>
29
- implements CrudApi<TResponse, TCreate, TUpdate, TQuery, TDeleteResponse>
33
+ implements
34
+ CrudApi<TResponse, TCreate, TUpdate, TQuery, TDeleteResponse, TCreateResponse, TUpdateResponse>
30
35
  {
31
36
  /**
32
37
  * Creates a new resource.
33
38
  * @permissions $resourceName.create
34
39
  */
35
- create(body: TCreate, headers?: Record<string, string>): Promise<TResponse> {
36
- return this.client.post<TResponse, TCreate>(this.basePath, body, headers)
40
+ create(body: TCreate, headers?: Record<string, string>): Promise<TCreateResponse> {
41
+ return this.client.post<TCreateResponse, TCreate>(this.basePath, body, headers)
37
42
  }
38
43
 
39
44
  /**
40
45
  * Updates an existing resource by ID.
41
46
  * @permissions $resourceName.update
42
47
  */
43
- updateById(id: string, body: TUpdate, headers?: Record<string, string>): Promise<TResponse> {
44
- return this.client.put<TResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)
48
+ updateById(
49
+ id: string,
50
+ body: TUpdate,
51
+ headers?: Record<string, string>,
52
+ ): Promise<TUpdateResponse> {
53
+ return this.client.put<TUpdateResponse, TUpdate>(`${this.basePath}/${id}`, body, headers)
45
54
  }
46
55
 
47
56
  /**
package/src/core/types.ts CHANGED
@@ -160,8 +160,10 @@ export type CrudApi<
160
160
  TUpdate = TCreate,
161
161
  TQuery extends ListQuery<TResponse> = ListQuery<TResponse>,
162
162
  TDeleteResponse = TResponse,
163
+ TCreateResponse = TResponse,
164
+ TUpdateResponse = TResponse,
163
165
  > = HasGetMany<TResponse, TQuery> &
164
166
  HasGetById<TResponse> &
165
- HasCreate<TResponse, TCreate> &
166
- HasUpdateById<TResponse, TUpdate> &
167
+ HasCreate<TCreateResponse, TCreate> &
168
+ HasUpdateById<TUpdateResponse, TUpdate> &
167
169
  HasDeleteById<TDeleteResponse>