@commercetools-frontend-extensions/operations 0.0.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 (158) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +169 -0
  3. package/babel.config.js +6 -0
  4. package/dist/commercetools-frontend-extensions-operations.cjs.d.ts +2 -0
  5. package/dist/commercetools-frontend-extensions-operations.cjs.dev.js +2469 -0
  6. package/dist/commercetools-frontend-extensions-operations.cjs.js +7 -0
  7. package/dist/commercetools-frontend-extensions-operations.cjs.prod.js +2461 -0
  8. package/dist/commercetools-frontend-extensions-operations.esm.js +2316 -0
  9. package/dist/declarations/src/@api/export-operations.d.ts +5 -0
  10. package/dist/declarations/src/@api/fetcher.d.ts +17 -0
  11. package/dist/declarations/src/@api/file-upload.d.ts +3 -0
  12. package/dist/declarations/src/@api/import-containers.d.ts +35 -0
  13. package/dist/declarations/src/@api/import-operations.d.ts +6 -0
  14. package/dist/declarations/src/@api/index.d.ts +8 -0
  15. package/dist/declarations/src/@api/process-file.d.ts +3 -0
  16. package/dist/declarations/src/@api/test-fixtures.d.ts +272 -0
  17. package/dist/declarations/src/@api/urls.d.ts +44 -0
  18. package/dist/declarations/src/@components/file-drop-area/active-drag-drop-area.d.ts +10 -0
  19. package/dist/declarations/src/@components/file-drop-area/disabled-drop-area.d.ts +5 -0
  20. package/dist/declarations/src/@components/file-drop-area/drop-area-wrapper.d.ts +11 -0
  21. package/dist/declarations/src/@components/file-drop-area/enabled-drop-area.d.ts +7 -0
  22. package/dist/declarations/src/@components/file-drop-area/file-drop-area.d.ts +14 -0
  23. package/dist/declarations/src/@components/file-drop-area/file-dropped-area.d.ts +6 -0
  24. package/dist/declarations/src/@components/file-drop-area/index.d.ts +7 -0
  25. package/dist/declarations/src/@components/file-drop-area/styles.d.ts +9 -0
  26. package/dist/declarations/src/@components/icons/file-icon.d.ts +2 -0
  27. package/dist/declarations/src/@components/icons/index.d.ts +2 -0
  28. package/dist/declarations/src/@components/icons/lock-icon.d.ts +2 -0
  29. package/dist/declarations/src/@components/index.d.ts +6 -0
  30. package/dist/declarations/src/@components/info-box/index.d.ts +1 -0
  31. package/dist/declarations/src/@components/info-box/info-box.d.ts +7 -0
  32. package/dist/declarations/src/@components/upload-separator/index.d.ts +1 -0
  33. package/dist/declarations/src/@components/upload-separator/upload-separator.d.ts +12 -0
  34. package/dist/declarations/src/@components/upload-settings/index.d.ts +1 -0
  35. package/dist/declarations/src/@components/upload-settings/upload-settings.d.ts +11 -0
  36. package/dist/declarations/src/@components/uploading-modal/index.d.ts +1 -0
  37. package/dist/declarations/src/@components/uploading-modal/uploading-modal.d.ts +12 -0
  38. package/dist/declarations/src/@constants/delimiters.d.ts +8 -0
  39. package/dist/declarations/src/@constants/import-tags.d.ts +7 -0
  40. package/dist/declarations/src/@constants/index.d.ts +4 -0
  41. package/dist/declarations/src/@constants/resource-links.d.ts +10 -0
  42. package/dist/declarations/src/@constants/upload-limits.d.ts +10 -0
  43. package/dist/declarations/src/@errors/http-error.d.ts +6 -0
  44. package/dist/declarations/src/@errors/index.d.ts +8 -0
  45. package/dist/declarations/src/@errors/invalid-response-error.d.ts +3 -0
  46. package/dist/declarations/src/@errors/no-resources-to-export-error.d.ts +3 -0
  47. package/dist/declarations/src/@errors/project-key-not-available-error.d.ts +3 -0
  48. package/dist/declarations/src/@errors/query-predicate-error.d.ts +4 -0
  49. package/dist/declarations/src/@errors/unexpected-column-error.d.ts +3 -0
  50. package/dist/declarations/src/@errors/unexpected-operation-state-error.d.ts +4 -0
  51. package/dist/declarations/src/@errors/unexpected-resource-type-error.d.ts +3 -0
  52. package/dist/declarations/src/@hooks/index.d.ts +5 -0
  53. package/dist/declarations/src/@hooks/use-fetch-export-operations.d.ts +15 -0
  54. package/dist/declarations/src/@hooks/use-fetch-import-container-details.d.ts +15 -0
  55. package/dist/declarations/src/@hooks/use-fetch-import-operations.d.ts +16 -0
  56. package/dist/declarations/src/@hooks/use-fetch-import-summaries.d.ts +20 -0
  57. package/dist/declarations/src/@hooks/use-import-container-upload.d.ts +18 -0
  58. package/dist/declarations/src/@types/api.d.ts +13 -0
  59. package/dist/declarations/src/@types/basic-error-data-type.d.ts +5 -0
  60. package/dist/declarations/src/@types/export-operation.d.ts +95 -0
  61. package/dist/declarations/src/@types/file-upload.d.ts +63 -0
  62. package/dist/declarations/src/@types/import-container.d.ts +53 -0
  63. package/dist/declarations/src/@types/import-operation.d.ts +13 -0
  64. package/dist/declarations/src/@types/import-states.d.ts +9 -0
  65. package/dist/declarations/src/@types/import-summary.d.ts +15 -0
  66. package/dist/declarations/src/@types/index.d.ts +9 -0
  67. package/dist/declarations/src/@types/shared.d.ts +7 -0
  68. package/dist/declarations/src/@utils/error-mapping.d.ts +19 -0
  69. package/dist/declarations/src/@utils/file-upload.d.ts +46 -0
  70. package/dist/declarations/src/@utils/form.d.ts +1 -0
  71. package/dist/declarations/src/@utils/format.d.ts +5 -0
  72. package/dist/declarations/src/@utils/import-container.d.ts +8 -0
  73. package/dist/declarations/src/@utils/index.d.ts +6 -0
  74. package/dist/declarations/src/@utils/url.d.ts +6 -0
  75. package/dist/declarations/src/index.d.ts +26 -0
  76. package/index.js +1 -0
  77. package/jest.test.config.js +11 -0
  78. package/package.json +63 -0
  79. package/src/@api/export-operations.ts +26 -0
  80. package/src/@api/fetcher.spec.ts +51 -0
  81. package/src/@api/fetcher.ts +127 -0
  82. package/src/@api/file-upload.spec.ts +83 -0
  83. package/src/@api/file-upload.ts +46 -0
  84. package/src/@api/import-containers.ts +256 -0
  85. package/src/@api/import-operations.ts +33 -0
  86. package/src/@api/index.ts +8 -0
  87. package/src/@api/process-file.spec.ts +74 -0
  88. package/src/@api/process-file.ts +53 -0
  89. package/src/@api/test-fixtures.ts +772 -0
  90. package/src/@api/urls.ts +118 -0
  91. package/src/@components/file-drop-area/active-drag-drop-area.tsx +33 -0
  92. package/src/@components/file-drop-area/disabled-drop-area.tsx +17 -0
  93. package/src/@components/file-drop-area/drop-area-wrapper.tsx +38 -0
  94. package/src/@components/file-drop-area/enabled-drop-area.tsx +27 -0
  95. package/src/@components/file-drop-area/file-drop-area.tsx +74 -0
  96. package/src/@components/file-drop-area/file-dropped-area.tsx +29 -0
  97. package/src/@components/file-drop-area/index.ts +7 -0
  98. package/src/@components/file-drop-area/styles.ts +67 -0
  99. package/src/@components/icons/file-icon.tsx +30 -0
  100. package/src/@components/icons/index.ts +2 -0
  101. package/src/@components/icons/lock-icon.tsx +34 -0
  102. package/src/@components/index.ts +6 -0
  103. package/src/@components/info-box/index.ts +1 -0
  104. package/src/@components/info-box/info-box.tsx +23 -0
  105. package/src/@components/upload-separator/index.ts +1 -0
  106. package/src/@components/upload-separator/upload-separator.tsx +61 -0
  107. package/src/@components/upload-settings/index.ts +1 -0
  108. package/src/@components/upload-settings/upload-settings.tsx +36 -0
  109. package/src/@components/uploading-modal/index.ts +1 -0
  110. package/src/@components/uploading-modal/uploading-modal.tsx +64 -0
  111. package/src/@constants/delimiters.ts +14 -0
  112. package/src/@constants/import-tags.ts +9 -0
  113. package/src/@constants/index.ts +4 -0
  114. package/src/@constants/resource-links.ts +61 -0
  115. package/src/@constants/upload-limits.ts +11 -0
  116. package/src/@errors/http-error.ts +17 -0
  117. package/src/@errors/index.ts +8 -0
  118. package/src/@errors/invalid-response-error.ts +6 -0
  119. package/src/@errors/no-resources-to-export-error.ts +6 -0
  120. package/src/@errors/project-key-not-available-error.ts +6 -0
  121. package/src/@errors/query-predicate-error.ts +10 -0
  122. package/src/@errors/unexpected-column-error.ts +6 -0
  123. package/src/@errors/unexpected-operation-state-error.ts +8 -0
  124. package/src/@errors/unexpected-resource-type-error.ts +6 -0
  125. package/src/@hooks/index.ts +5 -0
  126. package/src/@hooks/messages.ts +11 -0
  127. package/src/@hooks/use-fetch-export-operations.ts +34 -0
  128. package/src/@hooks/use-fetch-import-container-details.ts +31 -0
  129. package/src/@hooks/use-fetch-import-operations.ts +42 -0
  130. package/src/@hooks/use-fetch-import-summaries.ts +47 -0
  131. package/src/@hooks/use-fetch.spec.ts +76 -0
  132. package/src/@hooks/use-fetch.ts +80 -0
  133. package/src/@hooks/use-import-container-upload.spec.ts +294 -0
  134. package/src/@hooks/use-import-container-upload.ts +126 -0
  135. package/src/@types/api.ts +14 -0
  136. package/src/@types/basic-error-data-type.ts +5 -0
  137. package/src/@types/export-operation.ts +144 -0
  138. package/src/@types/file-upload.ts +81 -0
  139. package/src/@types/import-container.ts +104 -0
  140. package/src/@types/import-operation.ts +31 -0
  141. package/src/@types/import-states.ts +9 -0
  142. package/src/@types/import-summary.ts +22 -0
  143. package/src/@types/index.ts +9 -0
  144. package/src/@types/shared.ts +52 -0
  145. package/src/@utils/error-mapping.spec.ts +126 -0
  146. package/src/@utils/error-mapping.ts +39 -0
  147. package/src/@utils/file-upload.spec.ts +151 -0
  148. package/src/@utils/file-upload.ts +150 -0
  149. package/src/@utils/form.ts +20 -0
  150. package/src/@utils/format.spec.ts +62 -0
  151. package/src/@utils/format.ts +53 -0
  152. package/src/@utils/import-container.spec.ts +26 -0
  153. package/src/@utils/import-container.ts +34 -0
  154. package/src/@utils/index.ts +6 -0
  155. package/src/@utils/url.spec.ts +75 -0
  156. package/src/@utils/url.ts +18 -0
  157. package/src/index.ts +27 -0
  158. package/tsconfig.json +9 -0
@@ -0,0 +1,51 @@
1
+ import { HttpError } from '../@errors'
2
+ import { rest } from 'msw'
3
+ import { setupServer } from 'msw/node'
4
+ import { fetcher } from './fetcher'
5
+
6
+ const mockServer = setupServer(
7
+ rest.get('http://localhost:8080/proxy/import/hello', (_, res, ctx) =>
8
+ res(
9
+ ctx.json({
10
+ message: 'hello',
11
+ })
12
+ )
13
+ )
14
+ )
15
+ afterEach(() => {
16
+ mockServer.resetHandlers()
17
+ })
18
+ beforeAll(() => {
19
+ mockServer.listen()
20
+ })
21
+ afterAll(() => {
22
+ mockServer.close()
23
+ })
24
+
25
+ // TODO: write more tests
26
+ describe('fetcher', () => {
27
+ it('should add proxy prefix to url', async () => {
28
+ const response = await fetcher<{ message: string }>({
29
+ url: '/hello',
30
+ config: {
31
+ proxy: 'IMPORT',
32
+ },
33
+ })
34
+ expect(response.message).toBe('hello')
35
+ })
36
+ it('should throw error if response is unsuccessful', async () => {
37
+ mockServer.use(
38
+ rest.get('http://localhost:8080/proxy/import/hello', (_, res, ctx) => {
39
+ return res(ctx.status(404), ctx.json({ message: 'Not Found' }))
40
+ })
41
+ )
42
+ await expect(
43
+ fetcher<{ message: string }>({
44
+ url: '/hello',
45
+ config: {
46
+ proxy: 'IMPORT',
47
+ },
48
+ })
49
+ ).rejects.toThrow(new HttpError(404, 'Not Found'))
50
+ })
51
+ })
@@ -0,0 +1,127 @@
1
+ import {
2
+ buildApiUrl,
3
+ executeHttpClientRequest,
4
+ createHttpClientOptions,
5
+ } from '@commercetools-frontend/application-shell'
6
+ import { oidcStorage } from '@commercetools-frontend/application-shell-connectors'
7
+ import { type Fetcher } from '../@types'
8
+ import { HttpError } from '../@errors'
9
+
10
+ const addProxyPrefixToUrl = (uri: string, proxy?: string) => {
11
+ return proxy ? `/proxy/${proxy}${uri}` : uri
12
+ }
13
+
14
+ export const fetcher = async <Data>({ url, payload, config }: Fetcher) => {
15
+ const data = await executeHttpClientRequest<Promise<Data>>(
16
+ async (options: RequestInit) => {
17
+ const res = await fetch(
18
+ buildApiUrl(addProxyPrefixToUrl(url, config?.proxy)),
19
+ {
20
+ ...options,
21
+ method: config?.method,
22
+ body: payload,
23
+ signal: config?.abortSignal,
24
+ }
25
+ )
26
+ if (!res.ok) {
27
+ const errorData = await res.json()
28
+ throw new HttpError(res.status, res.statusText, errorData)
29
+ }
30
+ const data = res.json()
31
+ return {
32
+ data,
33
+ statusCode: res.status,
34
+ getHeader: (key: string) => res.headers.get(key),
35
+ }
36
+ },
37
+ {
38
+ headers: {
39
+ 'Content-Type': 'application/json',
40
+ ...config?.headers,
41
+ },
42
+ }
43
+ )
44
+ return data
45
+ }
46
+
47
+ export const fetchUsingXhr = ({
48
+ url,
49
+ payload,
50
+ config,
51
+ onProgress,
52
+ onSuccess,
53
+ onError,
54
+ }: {
55
+ url: string
56
+ payload: FormData
57
+ config: {
58
+ abortSignal?: AbortSignal
59
+ proxy?: string
60
+ method: string
61
+ headers?: { [key: string]: string | null }
62
+ }
63
+ onProgress: (progress: number) => void
64
+ onSuccess: (data: any) => void
65
+ onError: (error: Error) => void
66
+ }): XMLHttpRequest => {
67
+ const options = createHttpClientOptions({
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ ...config?.headers,
71
+ },
72
+ })
73
+
74
+ const xhr = new XMLHttpRequest()
75
+ xhr.open(
76
+ config?.method,
77
+ buildApiUrl(addProxyPrefixToUrl(url, config?.proxy)),
78
+ true
79
+ )
80
+
81
+ xhr.withCredentials = true
82
+
83
+ if (options.headers) {
84
+ Object.keys(options.headers).forEach((key) => {
85
+ xhr.setRequestHeader(key, options.headers[key])
86
+ })
87
+ }
88
+
89
+ xhr.upload.onprogress = function (event) {
90
+ if (event.lengthComputable) {
91
+ const percentComplete = (event.loaded / event.total) * 100
92
+ onProgress(percentComplete)
93
+ }
94
+ }
95
+
96
+ const throwError = (errorData: Response) => {
97
+ onError(new HttpError(xhr.status, xhr.statusText, errorData))
98
+ }
99
+
100
+ xhr.onload = function () {
101
+ const data = JSON.parse(xhr.responseText)
102
+ // Code copied from `executeHttpClientRequest` to replicate the same behavior
103
+ const refreshedSessionToken = xhr.getResponseHeader(
104
+ 'x-refreshed-session-token'
105
+ )
106
+ if (refreshedSessionToken) {
107
+ oidcStorage.setActiveSession(refreshedSessionToken)
108
+ }
109
+ if (xhr.status >= 200 && xhr.status < 300) {
110
+ onSuccess(data)
111
+ } else {
112
+ throwError(data)
113
+ }
114
+ }
115
+
116
+ xhr.onerror = function () {
117
+ const errorData = JSON.parse(xhr.responseText)
118
+ throwError(errorData)
119
+ }
120
+
121
+ xhr.onabort = function () {
122
+ onError(new DOMException('Aborted', 'AbortError'))
123
+ }
124
+
125
+ xhr.send(payload)
126
+ return xhr
127
+ }
@@ -0,0 +1,83 @@
1
+ import { waitFor } from '@testing-library/react'
2
+ import { rest } from 'msw'
3
+ import { setupServer } from 'msw/node'
4
+ import { uploadFileForImport } from './file-upload'
5
+ import type { FileUploadResponse } from '../@types'
6
+
7
+ const validFileUploadResponse: FileUploadResponse = {
8
+ valid: 1,
9
+ results: [],
10
+ invalid: 0,
11
+ fileName: 'file.csv',
12
+ itemsCount: 0,
13
+ rowsCount: 0,
14
+ columnsCount: 0,
15
+ columns: [],
16
+ ignoredColumns: [],
17
+ }
18
+
19
+ type ResourceTypeEntry = {
20
+ name: string
21
+ apiPath: string
22
+ }
23
+
24
+ const resourceTypes: Array<ResourceTypeEntry> = [
25
+ {
26
+ name: 'category',
27
+ apiPath: 'categories',
28
+ },
29
+ {
30
+ name: 'product',
31
+ apiPath: 'products',
32
+ },
33
+ {
34
+ name: 'inventory',
35
+ apiPath: 'inventories',
36
+ },
37
+ ]
38
+
39
+ const mockServer = setupServer(
40
+ ...resourceTypes.map(({ apiPath }) =>
41
+ rest.post(
42
+ `http://localhost:8080/proxy/import/test-with-big-data-key/${apiPath}/import-containers/container-key/file-upload`,
43
+ (_, res, ctx) => res(ctx.json(validFileUploadResponse))
44
+ )
45
+ )
46
+ )
47
+
48
+ afterEach(() => {
49
+ mockServer.resetHandlers()
50
+ })
51
+
52
+ beforeAll(() => {
53
+ mockServer.listen()
54
+ })
55
+
56
+ afterAll(() => {
57
+ mockServer.close()
58
+ })
59
+
60
+ describe('uploadFileForImport', () => {
61
+ const projectKey = 'test-with-big-data-key'
62
+ const containerKey = 'container-key'
63
+ const file: File = new File([], 'file.csv')
64
+
65
+ it.each([['category'], ['product'], ['inventory']])(
66
+ 'should post the file to the correct endpoint for the resource type %s',
67
+ async (resourceType) => {
68
+ const onSuccess = jest.fn()
69
+ uploadFileForImport({
70
+ projectKey,
71
+ importContainerKey: containerKey,
72
+ resourceType,
73
+ file,
74
+ onSuccess,
75
+ onProgress: jest.fn(),
76
+ onError: jest.fn(),
77
+ })
78
+ await waitFor(() =>
79
+ expect(onSuccess).toHaveBeenCalledWith(validFileUploadResponse)
80
+ )
81
+ }
82
+ )
83
+ })
@@ -0,0 +1,46 @@
1
+ import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants'
2
+ import {
3
+ type FileUploadResponse,
4
+ type FileUploadRequestParameters,
5
+ hasRequiredFields,
6
+ } from '../@types'
7
+ import { fetchUsingXhr } from './fetcher'
8
+ import { getFileUploadURL } from './urls'
9
+
10
+ export function uploadFileForImport({
11
+ projectKey,
12
+ importContainerKey,
13
+ resourceType,
14
+ file,
15
+ abortSignal,
16
+ onSuccess,
17
+ onProgress,
18
+ onError,
19
+ }: FileUploadRequestParameters): XMLHttpRequest {
20
+ const uri = getFileUploadURL({ projectKey, resourceType, importContainerKey })
21
+ const formData = new FormData()
22
+ formData.append('file', file, file.name)
23
+ return fetchUsingXhr({
24
+ url: uri,
25
+ payload: formData,
26
+ config: {
27
+ abortSignal,
28
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': null,
32
+ },
33
+ },
34
+ onProgress,
35
+ onSuccess,
36
+ onError,
37
+ })
38
+ }
39
+
40
+ export function assertFileUploadResponse(
41
+ maybeFileUploadResponse: unknown
42
+ ): asserts maybeFileUploadResponse is FileUploadResponse {
43
+ const requiredFields = ['results', 'valid']
44
+ if (hasRequiredFields(maybeFileUploadResponse, requiredFields)) return
45
+ throw new Error('Invalid response')
46
+ }
@@ -0,0 +1,256 @@
1
+ import type {
2
+ ImportContainer,
3
+ ImportContainerPagedResponse,
4
+ } from '@commercetools/importapi-sdk'
5
+ import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants'
6
+ import {
7
+ assertImportContainer,
8
+ assertImportContainerPagedResponse,
9
+ assertImportSummary,
10
+ ImportStates,
11
+ type ImportContainerQueryParams,
12
+ type CancelContainerResponse,
13
+ type ImportSummary,
14
+ type ExtendedImportContainer,
15
+ type ExtendedImportContainerDraft,
16
+ type ImportContainerDetails,
17
+ type ImportSummaries,
18
+ assertCancelContainerResponse,
19
+ } from '../@types'
20
+ import {
21
+ getImportContainersURL,
22
+ getImportContainerByKeyURL,
23
+ getImportSummaryURL,
24
+ getCreateImportContainerURL,
25
+ getDeleteImportContainerURL,
26
+ getImportContainerTasksURL,
27
+ } from './urls'
28
+ import { checkIfFileUploadImport } from '../@utils'
29
+ import { fetcher } from './fetcher'
30
+
31
+ export function getImportState(importSummary: ImportSummary): ImportStates {
32
+ const processing = importSummary.states.processing > 0
33
+ if (processing) return ImportStates.Processing
34
+
35
+ const waitForUnresolvedReferences =
36
+ importSummary.states.waitForMasterVariant > 0 ||
37
+ importSummary.states.unresolved > 0
38
+ if (waitForUnresolvedReferences)
39
+ return ImportStates.WaitForUnresolvedReferences
40
+
41
+ const partiallyCompleted =
42
+ (importSummary.states.imported > 0 &&
43
+ importSummary.states.imported < importSummary.total) ||
44
+ (importSummary.states.deleted > 0 &&
45
+ importSummary.states.deleted < importSummary.total)
46
+ if (partiallyCompleted) return ImportStates.PartiallyCompleted
47
+
48
+ const noRunning = importSummary.total === 0
49
+ if (noRunning) return ImportStates.NoRunningImports
50
+
51
+ const successfullyCompleted =
52
+ importSummary.states.imported === importSummary.total ||
53
+ importSummary.states.deleted === importSummary.total
54
+ if (successfullyCompleted) return ImportStates.SuccessfullyCompleted
55
+
56
+ const failed =
57
+ importSummary.states.rejected + importSummary.states.validationFailed ===
58
+ importSummary.total
59
+ if (failed) return ImportStates.Failed
60
+
61
+ const canceled = importSummary.states.canceled > 0
62
+ if (canceled) return ImportStates.Canceled
63
+
64
+ throw new Error(`Unsupported state ${JSON.stringify(importSummary.states)}`)
65
+ }
66
+
67
+ export function createImportContainerForFileUpload({
68
+ importContainerDraft,
69
+ projectKey,
70
+ }: {
71
+ importContainerDraft: ExtendedImportContainerDraft
72
+ projectKey: string
73
+ }): Promise<unknown> {
74
+ return fetcher({
75
+ url: getCreateImportContainerURL({ projectKey }),
76
+ payload: JSON.stringify({
77
+ retentionPolicy: {
78
+ strategy: 'ttl',
79
+ config: {
80
+ timeToLive: '54h', // 2 days and 6 hours
81
+ },
82
+ },
83
+ ...importContainerDraft,
84
+ }),
85
+ config: {
86
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
87
+ method: 'POST',
88
+ headers: {
89
+ accept: 'application/json',
90
+ 'Content-Type': 'application/json',
91
+ },
92
+ },
93
+ })
94
+ }
95
+
96
+ export function deleteImportContainer({
97
+ projectKey,
98
+ importContainerKey,
99
+ }: {
100
+ projectKey: string
101
+ importContainerKey: string
102
+ }): Promise<unknown> {
103
+ return fetcher({
104
+ url: getDeleteImportContainerURL({ projectKey, importContainerKey }),
105
+ config: {
106
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
107
+ method: 'DELETE',
108
+ headers: {
109
+ accept: 'application/json',
110
+ },
111
+ },
112
+ })
113
+ }
114
+
115
+ export async function fetchImportSummary({
116
+ projectKey,
117
+ importContainerKey,
118
+ }: {
119
+ projectKey: string
120
+ importContainerKey: string
121
+ }): Promise<ImportSummary> {
122
+ const importSummary = await fetcher<ImportSummary>({
123
+ url: getImportSummaryURL({ projectKey, importContainerKey }),
124
+ config: {
125
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
126
+ method: 'GET',
127
+ },
128
+ })
129
+
130
+ assertImportSummary(importSummary)
131
+
132
+ return importSummary
133
+ }
134
+
135
+ export async function fetchImportContainers({
136
+ projectKey,
137
+ queryParams,
138
+ }: {
139
+ projectKey: string
140
+ queryParams: ImportContainerQueryParams
141
+ }): Promise<ImportContainerPagedResponse> {
142
+ const importContainers = await fetcher<ImportContainerPagedResponse>({
143
+ url: getImportContainersURL({ projectKey, queryParams }),
144
+ config: {
145
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
146
+ method: 'GET',
147
+ },
148
+ })
149
+ assertImportContainerPagedResponse(importContainers)
150
+
151
+ return importContainers
152
+ }
153
+
154
+ export async function fetchImportSummaries({
155
+ projectKey,
156
+ queryParams,
157
+ }: {
158
+ projectKey: string
159
+ queryParams: ImportContainerQueryParams
160
+ }): Promise<ImportSummaries> {
161
+ const importContainers = await fetchImportContainers({
162
+ projectKey,
163
+ queryParams,
164
+ })
165
+
166
+ const results: Array<Promise<ImportContainerDetails>> =
167
+ importContainers.results.map(async (importContainer) => {
168
+ return await importContainerToContainerDetails(
169
+ projectKey,
170
+ importContainer
171
+ )
172
+ })
173
+
174
+ return {
175
+ results,
176
+ count: importContainers.count,
177
+ total: importContainers.total ?? 0,
178
+ queryParams: {
179
+ limit: importContainers.limit,
180
+ offset: importContainers.offset,
181
+ },
182
+ }
183
+ }
184
+
185
+ export async function fetchImportContainerDetails({
186
+ projectKey,
187
+ importContainerKey,
188
+ }: {
189
+ projectKey: string
190
+ importContainerKey: string
191
+ }): Promise<ImportContainerDetails> {
192
+ const importContainer = await fetchImportContainerByKey({
193
+ projectKey,
194
+ importContainerKey,
195
+ })
196
+ return await importContainerToContainerDetails(projectKey, importContainer)
197
+ }
198
+
199
+ export async function fetchImportContainerByKey({
200
+ projectKey,
201
+ importContainerKey,
202
+ }: {
203
+ projectKey: string
204
+ importContainerKey: string
205
+ }): Promise<ImportContainer> {
206
+ const importContainer = await fetcher<ImportContainer>({
207
+ url: getImportContainerByKeyURL({ projectKey, importContainerKey }),
208
+ config: {
209
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
210
+ method: 'GET',
211
+ },
212
+ })
213
+ assertImportContainer(importContainer)
214
+
215
+ return importContainer
216
+ }
217
+
218
+ export async function cancelImportContainerByKey({
219
+ projectKey,
220
+ importContainerKey,
221
+ }: {
222
+ projectKey: string
223
+ importContainerKey: string
224
+ }): Promise<CancelContainerResponse> {
225
+ const response = await fetcher<CancelContainerResponse>({
226
+ url: getImportContainerTasksURL({ projectKey, importContainerKey }),
227
+ payload: JSON.stringify({
228
+ task: 'cancel',
229
+ }),
230
+ config: {
231
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
232
+ method: 'POST',
233
+ },
234
+ })
235
+ assertCancelContainerResponse(response)
236
+ return response
237
+ }
238
+
239
+ async function importContainerToContainerDetails(
240
+ projectKey: string,
241
+ importContainer: ExtendedImportContainer
242
+ ): Promise<ImportContainerDetails> {
243
+ const importSummary = await fetchImportSummary({
244
+ projectKey,
245
+ importContainerKey: importContainer.key,
246
+ })
247
+ const importState = getImportState(importSummary)
248
+ const isFileUploadImport = checkIfFileUploadImport(importContainer.tags)
249
+
250
+ return {
251
+ importContainer: importContainer,
252
+ importState,
253
+ importSummary,
254
+ isFileUploadImport,
255
+ }
256
+ }
@@ -0,0 +1,33 @@
1
+ import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants'
2
+ import {
3
+ assertImportOperationPagedResponse,
4
+ type ExtendedImportOperationPagedResponse,
5
+ type ImportOperationQueryParams,
6
+ } from '../@types'
7
+ import { fetcher } from './fetcher'
8
+ import { getImportOperationsURL } from './urls'
9
+
10
+ export async function fetchImportOperations({
11
+ projectKey,
12
+ importContainerKey,
13
+ queryParams,
14
+ }: {
15
+ projectKey: string
16
+ importContainerKey: string
17
+ queryParams: ImportOperationQueryParams
18
+ }): Promise<ExtendedImportOperationPagedResponse> {
19
+ const importOperations = await fetcher<ExtendedImportOperationPagedResponse>({
20
+ url: getImportOperationsURL({
21
+ projectKey,
22
+ importContainerKey,
23
+ queryParams,
24
+ }),
25
+ config: {
26
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
27
+ method: 'GET',
28
+ },
29
+ })
30
+ assertImportOperationPagedResponse(importOperations)
31
+
32
+ return importOperations
33
+ }
@@ -0,0 +1,8 @@
1
+ export * from './fetcher'
2
+ export * from './file-upload'
3
+ export * from './import-containers'
4
+ export * from './import-operations'
5
+ export * from './export-operations'
6
+ export * from './process-file'
7
+ export * from './urls'
8
+ export * from './test-fixtures'
@@ -0,0 +1,74 @@
1
+ import { plural } from 'pluralize'
2
+ import { rest } from 'msw'
3
+ import { setupServer } from 'msw/node'
4
+ import { processUploadedFile } from './process-file'
5
+ import { validProcessFileResponse } from './test-fixtures'
6
+
7
+ const mockServer = setupServer()
8
+
9
+ beforeAll(() => {
10
+ mockServer.listen()
11
+ })
12
+ afterEach(() => {
13
+ mockServer.resetHandlers()
14
+ })
15
+ afterAll(() => {
16
+ mockServer.close()
17
+ })
18
+
19
+ describe('processUploadedFile', () => {
20
+ const projectKey = 'test-with-big-data'
21
+ const containerKey = 'container-key'
22
+ const resourceType = 'category'
23
+
24
+ it('should process the file with the correct endpoint for the current project', async () => {
25
+ mockServer.use(
26
+ rest.post(
27
+ `http://localhost:8080/proxy/import/${projectKey}/${plural(
28
+ resourceType
29
+ )}/import-containers/${containerKey}/process-file`,
30
+ (_, res, ctx) => {
31
+ return res(ctx.json(validProcessFileResponse))
32
+ }
33
+ )
34
+ )
35
+
36
+ const response = await processUploadedFile({
37
+ projectKey,
38
+ importContainerKey: containerKey,
39
+ resourceType,
40
+ })
41
+
42
+ expect(response.message).toEqual(validProcessFileResponse.message)
43
+ })
44
+
45
+ it('should process the file with delete action using the tasks endpoint', async () => {
46
+ let requestBody: any
47
+
48
+ mockServer.use(
49
+ rest.post(
50
+ `http://localhost:8080/proxy/import/${projectKey}/import-containers/${containerKey}/tasks`,
51
+ async (req, res, ctx) => {
52
+ requestBody = await req.json()
53
+ return res(ctx.json(validProcessFileResponse))
54
+ }
55
+ )
56
+ )
57
+
58
+ const response = await processUploadedFile({
59
+ projectKey,
60
+ importContainerKey: containerKey,
61
+ resourceType,
62
+ action: 'delete',
63
+ })
64
+
65
+ expect(response.message).toEqual(validProcessFileResponse.message)
66
+ expect(requestBody).toEqual({
67
+ task: 'process-file',
68
+ parameter: {
69
+ resourceType,
70
+ action: 'delete',
71
+ },
72
+ })
73
+ })
74
+ })