@ikatec/digisac-api-sdk 4.0.0 → 4.1.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 (130) hide show
  1. package/README.md +29 -2
  2. package/dist/apis/authHistory/AuthHistoryApi.cjs +3 -9
  3. package/dist/apis/authHistory/AuthHistoryApi.cjs.map +1 -1
  4. package/dist/apis/authHistory/AuthHistoryApi.d.ts +3 -4
  5. package/dist/apis/authHistory/AuthHistoryApi.d.ts.map +1 -1
  6. package/dist/apis/authHistory/AuthHistoryApi.mjs +3 -7
  7. package/dist/apis/authHistory/AuthHistoryApi.mjs.map +1 -1
  8. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs +2 -5
  9. package/dist/apis/clientFeedback/ClientFeedbackApi.cjs.map +1 -1
  10. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts +2 -4
  11. package/dist/apis/clientFeedback/ClientFeedbackApi.d.ts.map +1 -1
  12. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs +3 -5
  13. package/dist/apis/clientFeedback/ClientFeedbackApi.mjs.map +1 -1
  14. package/dist/apis/contacts/ContactsApi.cjs +2 -2
  15. package/dist/apis/contacts/ContactsApi.cjs.map +1 -1
  16. package/dist/apis/contacts/ContactsApi.mjs +2 -2
  17. package/dist/apis/contacts/ContactsApi.mjs.map +1 -1
  18. package/dist/apis/me/MeApi.cjs +2 -5
  19. package/dist/apis/me/MeApi.cjs.map +1 -1
  20. package/dist/apis/me/MeApi.d.ts +2 -4
  21. package/dist/apis/me/MeApi.d.ts.map +1 -1
  22. package/dist/apis/me/MeApi.mjs +3 -5
  23. package/dist/apis/me/MeApi.mjs.map +1 -1
  24. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs +3 -9
  25. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.cjs.map +1 -1
  26. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts +3 -4
  27. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.d.ts.map +1 -1
  28. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs +3 -7
  29. package/dist/apis/serviceAccessManagement/ServiceAccessManagementApi.mjs.map +1 -1
  30. package/dist/apis/stickerUsers/StickerUsersApi.cjs +3 -9
  31. package/dist/apis/stickerUsers/StickerUsersApi.cjs.map +1 -1
  32. package/dist/apis/stickerUsers/StickerUsersApi.d.ts +3 -4
  33. package/dist/apis/stickerUsers/StickerUsersApi.d.ts.map +1 -1
  34. package/dist/apis/stickerUsers/StickerUsersApi.mjs +3 -7
  35. package/dist/apis/stickerUsers/StickerUsersApi.mjs.map +1 -1
  36. package/dist/apis/terms/TermsApi.cjs +3 -12
  37. package/dist/apis/terms/TermsApi.cjs.map +1 -1
  38. package/dist/apis/terms/TermsApi.d.ts +2 -4
  39. package/dist/apis/terms/TermsApi.d.ts.map +1 -1
  40. package/dist/apis/terms/TermsApi.mjs +3 -10
  41. package/dist/apis/terms/TermsApi.mjs.map +1 -1
  42. package/dist/apis/transcripts/TranscriptsApi.cjs +2 -5
  43. package/dist/apis/transcripts/TranscriptsApi.cjs.map +1 -1
  44. package/dist/apis/transcripts/TranscriptsApi.d.ts +2 -4
  45. package/dist/apis/transcripts/TranscriptsApi.d.ts.map +1 -1
  46. package/dist/apis/transcripts/TranscriptsApi.mjs +3 -5
  47. package/dist/apis/transcripts/TranscriptsApi.mjs.map +1 -1
  48. package/dist/core/ApiClient.d.ts +3 -0
  49. package/dist/core/ApiClient.d.ts.map +1 -1
  50. package/dist/core/ArchivableCrudApi.cjs +0 -2
  51. package/dist/core/ArchivableCrudApi.cjs.map +1 -1
  52. package/dist/core/ArchivableCrudApi.d.ts.map +1 -1
  53. package/dist/core/ArchivableCrudApi.mjs +0 -2
  54. package/dist/core/ArchivableCrudApi.mjs.map +1 -1
  55. package/dist/core/BaseApi.cjs +34 -0
  56. package/dist/core/BaseApi.cjs.map +1 -0
  57. package/dist/core/BaseApi.d.ts +20 -0
  58. package/dist/core/BaseApi.d.ts.map +1 -0
  59. package/dist/core/BaseApi.mjs +33 -0
  60. package/dist/core/BaseApi.mjs.map +1 -0
  61. package/dist/core/BaseApiClient.cjs +4 -1
  62. package/dist/core/BaseApiClient.cjs.map +1 -1
  63. package/dist/core/BaseApiClient.d.ts +7 -1
  64. package/dist/core/BaseApiClient.d.ts.map +1 -1
  65. package/dist/core/BaseApiClient.mjs +4 -1
  66. package/dist/core/BaseApiClient.mjs.map +1 -1
  67. package/dist/core/BaseCrudApi.cjs +6 -16
  68. package/dist/core/BaseCrudApi.cjs.map +1 -1
  69. package/dist/core/BaseCrudApi.d.ts +3 -2
  70. package/dist/core/BaseCrudApi.d.ts.map +1 -1
  71. package/dist/core/BaseCrudApi.mjs +6 -14
  72. package/dist/core/BaseCrudApi.mjs.map +1 -1
  73. package/dist/core/bindMethods.cjs +24 -0
  74. package/dist/core/bindMethods.cjs.map +1 -0
  75. package/dist/core/bindMethods.d.ts +7 -0
  76. package/dist/core/bindMethods.d.ts.map +1 -0
  77. package/dist/core/bindMethods.mjs +22 -0
  78. package/dist/core/bindMethods.mjs.map +1 -0
  79. package/dist/core/index.cjs +2 -0
  80. package/dist/core/index.d.ts +3 -0
  81. package/dist/core/index.d.ts.map +1 -1
  82. package/dist/core/index.mjs +2 -1
  83. package/dist/core/queryString.cjs +22 -0
  84. package/dist/core/queryString.cjs.map +1 -0
  85. package/dist/core/queryString.d.ts +14 -0
  86. package/dist/core/queryString.d.ts.map +1 -0
  87. package/dist/core/queryString.mjs +18 -0
  88. package/dist/core/queryString.mjs.map +1 -0
  89. package/dist/core/types.d.ts +2 -0
  90. package/dist/core/types.d.ts.map +1 -1
  91. package/dist/core/withQueryFormat.cjs +42 -0
  92. package/dist/core/withQueryFormat.cjs.map +1 -0
  93. package/dist/core/withQueryFormat.d.ts +10 -0
  94. package/dist/core/withQueryFormat.d.ts.map +1 -0
  95. package/dist/core/withQueryFormat.mjs +40 -0
  96. package/dist/core/withQueryFormat.mjs.map +1 -0
  97. package/dist/index.cjs +2 -0
  98. package/dist/index.mjs +2 -1
  99. package/package.json +1 -1
  100. package/src/apis/authHistory/AuthHistoryApi.test.ts +1 -1
  101. package/src/apis/authHistory/AuthHistoryApi.ts +10 -10
  102. package/src/apis/campaigns/CampaignsApi.test.ts +1 -1
  103. package/src/apis/clientFeedback/ClientFeedbackApi.ts +5 -7
  104. package/src/apis/contacts/ContactsApi.test.ts +1 -1
  105. package/src/apis/contacts/ContactsApi.ts +2 -2
  106. package/src/apis/me/MeApi.ts +2 -8
  107. package/src/apis/messages/MessagesApi.test.ts +2 -2
  108. package/src/apis/queryFormat.test.ts +198 -0
  109. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.test.ts +2 -2
  110. package/src/apis/serviceAccessManagement/ServiceAccessManagementApi.ts +10 -10
  111. package/src/apis/stickerUsers/StickerUsersApi.test.ts +1 -1
  112. package/src/apis/stickerUsers/StickerUsersApi.ts +7 -10
  113. package/src/apis/terms/TermsApi.ts +3 -12
  114. package/src/apis/transcripts/TranscriptsApi.ts +2 -7
  115. package/src/core/ApiClient.ts +4 -0
  116. package/src/core/ArchivableCrudApi.ts +0 -2
  117. package/src/core/BaseApi.test.ts +164 -0
  118. package/src/core/BaseApi.ts +32 -0
  119. package/src/core/BaseApiClient.test.ts +10 -0
  120. package/src/core/BaseApiClient.ts +9 -1
  121. package/src/core/BaseCrudApi.test-d.ts +9 -0
  122. package/src/core/BaseCrudApi.test.ts +131 -60
  123. package/src/core/BaseCrudApi.ts +14 -14
  124. package/src/core/bindMethods.ts +26 -0
  125. package/src/core/index.ts +3 -0
  126. package/src/core/queryString.test.ts +71 -0
  127. package/src/core/queryString.ts +30 -0
  128. package/src/core/types.ts +5 -0
  129. package/src/core/withQueryFormat.test.ts +136 -0
  130. package/src/core/withQueryFormat.ts +47 -0
@@ -0,0 +1,136 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import type { ApiClient } from './ApiClient'
3
+ import { BaseApiClient } from './BaseApiClient'
4
+ import { BaseCrudApi } from './BaseCrudApi'
5
+ import { StickerUsersApi } from '../apis/stickerUsers/StickerUsersApi'
6
+ import { TermsApi } from '../apis/terms/TermsApi'
7
+
8
+ type Item = { id: string; name: string }
9
+
10
+ class ItemsApi extends BaseCrudApi<Item, { name: string }> {
11
+ constructor(client: ApiClient) {
12
+ super(client, '/items')
13
+ this.searchByName = this.searchByName.bind(this)
14
+ }
15
+
16
+ searchByName(name: string) {
17
+ return this.getMany({ where: { name }, paginate: false })
18
+ }
19
+ }
20
+
21
+ function createMockClient(): ApiClient {
22
+ return {
23
+ setAccessToken: vi.fn().mockReturnThis(),
24
+ request: vi.fn(),
25
+ get: vi.fn().mockResolvedValue([]),
26
+ post: vi.fn(),
27
+ put: vi.fn(),
28
+ patch: vi.fn(),
29
+ delete: vi.fn(),
30
+ }
31
+ }
32
+
33
+ const calledUrl = (client: ApiClient, call = 0) =>
34
+ decodeURIComponent(vi.mocked(client.get).mock.calls.at(call)?.at(0) as string)
35
+
36
+ const JSON_QUERY = '/items?query={"where":{"name":"x"},"paginate":false}'
37
+ const QS_QUERY = '/items?where[name]=x&paginate=false'
38
+
39
+ describe('withQueryFormat', () => {
40
+ let client: ApiClient
41
+ let api: ItemsApi
42
+
43
+ beforeEach(() => {
44
+ client = createMockClient()
45
+ api = new ItemsApi(client)
46
+ })
47
+
48
+ it('returns a copy that uses the given format', async () => {
49
+ await api.withQueryFormat('qs').getMany({ where: { name: 'x' }, paginate: false })
50
+ expect(calledUrl(client)).toBe(QS_QUERY)
51
+ })
52
+
53
+ it('leaves the original API unchanged', async () => {
54
+ api.withQueryFormat('qs')
55
+ await api.getMany({ where: { name: 'x' }, paginate: false })
56
+ expect(calledUrl(client)).toBe(JSON_QUERY)
57
+ expect(api.client).toBe(client)
58
+ })
59
+
60
+ it('keeps the class, basePath and fields', () => {
61
+ const copy = api.withQueryFormat('qs')
62
+ expect(copy).toBeInstanceOf(ItemsApi)
63
+ expect(copy).toBeInstanceOf(BaseCrudApi)
64
+ expect(copy.basePath).toBe('/items')
65
+ expect(copy).not.toBe(api)
66
+ })
67
+
68
+ it('binds destructured methods to the copy', async () => {
69
+ const { getMany, getOne, getById } = api.withQueryFormat('qs')
70
+ await getMany({ where: { name: 'x' }, paginate: false })
71
+ await getOne({ where: { name: 'x' } })
72
+ await getById('1', { attributes: ['id'] })
73
+ expect(calledUrl(client, 0)).toBe(QS_QUERY)
74
+ expect(calledUrl(client, 1)).toBe('/items?where[name]=x&limit=1&paginate=false')
75
+ expect(calledUrl(client, 2)).toBe('/items/1?attributes[0]=id')
76
+ })
77
+
78
+ it('rebinds methods bound by subclass constructors', async () => {
79
+ const { searchByName } = api.withQueryFormat('qs')
80
+ await searchByName('x')
81
+ expect(calledUrl(client)).toBe(QS_QUERY)
82
+ })
83
+
84
+ it('can be chained again', async () => {
85
+ await api.withQueryFormat('qs').withQueryFormat('json').searchByName('x')
86
+ expect(calledUrl(client)).toBe(JSON_QUERY)
87
+ })
88
+
89
+ it('overrides a qs client back to json', async () => {
90
+ const qsApi = new ItemsApi(Object.assign(createMockClient(), { queryFormat: 'qs' as const }))
91
+ await qsApi.withQueryFormat('json').searchByName('x')
92
+ expect(calledUrl(qsApi.client)).toBe(JSON_QUERY)
93
+ })
94
+
95
+ it('still sends requests through the original client', async () => {
96
+ const copy = api.withQueryFormat('qs')
97
+ await copy.create({ name: 'x' })
98
+ expect(client.post).toHaveBeenCalledWith('/items', { name: 'x' }, undefined)
99
+ expect(copy.client.queryFormat).toBe('qs')
100
+ expect(client.queryFormat).toBeUndefined()
101
+ })
102
+
103
+ it('works on APIs that do not extend BaseCrudApi', async () => {
104
+ await new StickerUsersApi(client).withQueryFormat('qs').getMany({ page: 2 })
105
+ await new TermsApi(client).withQueryFormat('qs').getById('t1', { attributes: ['id'] })
106
+ expect(calledUrl(client, 0)).toBe('/sticker-users?page=2')
107
+ expect(calledUrl(client, 1)).toBe('/terms/t1?attributes[0]=id')
108
+ })
109
+
110
+ describe('with BaseApiClient', () => {
111
+ afterEach(() => {
112
+ vi.restoreAllMocks()
113
+ })
114
+
115
+ const mockFetch = () =>
116
+ vi
117
+ .spyOn(globalThis, 'fetch')
118
+ .mockResolvedValue(
119
+ new Response('[]', { status: 200, headers: { 'Content-Type': 'application/json' } }),
120
+ )
121
+
122
+ it('uses the base URL and a token set on the original client after copying', async () => {
123
+ const fetchSpy = mockFetch()
124
+ const baseClient = new BaseApiClient('https://api.example.com', 'old-token')
125
+ const copy = new ItemsApi(baseClient).withQueryFormat('qs')
126
+ baseClient.setAccessToken('new-token')
127
+
128
+ await copy.searchByName('x')
129
+
130
+ const [url, init] = fetchSpy.mock.calls[0] as [string, RequestInit]
131
+ expect(decodeURIComponent(url)).toBe(`https://api.example.com${QS_QUERY}`)
132
+ expect((init.headers as Record<string, string>).Authorization).toBe('Bearer new-token')
133
+ expect(baseClient.queryFormat).toBe('json')
134
+ })
135
+ })
136
+ })
@@ -0,0 +1,47 @@
1
+ import type { ApiClient } from './ApiClient'
2
+ import type { QueryFormat } from './queryString'
3
+
4
+ /**
5
+ * A view of `client` that serializes queries in `format`. Everything else — methods, base URL and
6
+ * the access token — is read from `client`, so `client.setAccessToken()` still applies to it.
7
+ */
8
+ function overrideQueryFormat<T extends ApiClient>(client: T, format: QueryFormat): T {
9
+ return Object.create(client, {
10
+ queryFormat: { value: format, enumerable: true, configurable: true },
11
+ })
12
+ }
13
+
14
+ /**
15
+ * Copy of `api` whose client serializes queries in `format`; `api` itself is untouched.
16
+ *
17
+ * The copy has the same prototype and fields as `api`, with `client` replaced. Methods the
18
+ * constructor bound to `api` (`this.getMany = this.getMany.bind(this)`) are bound again to the
19
+ * copy, so they use its client.
20
+ */
21
+ export function cloneWithQueryFormat<T extends object>(api: T, format: QueryFormat): T {
22
+ const source = api as T & { client: ApiClient }
23
+ const prototype = Object.getPrototypeOf(api) as Record<PropertyKey, unknown>
24
+ const copy = Object.create(prototype) as Record<PropertyKey, unknown>
25
+
26
+ for (const key of Reflect.ownKeys(api)) {
27
+ Object.defineProperty(
28
+ copy,
29
+ key,
30
+ Object.getOwnPropertyDescriptor(api, key) as PropertyDescriptor,
31
+ )
32
+ }
33
+ Object.defineProperty(copy, 'client', {
34
+ value: overrideQueryFormat(source.client, format),
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: false,
38
+ })
39
+ for (const key of Reflect.ownKeys(api)) {
40
+ const method = prototype[key]
41
+ if (typeof copy[key] === 'function' && typeof method === 'function') {
42
+ copy[key] = method.bind(copy)
43
+ }
44
+ }
45
+
46
+ return copy as T
47
+ }