@commercetools-frontend-extensions/operations 0.0.0-canary-20251209161906
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/README.md +178 -0
- package/babel.config.js +6 -0
- package/dist/commercetools-frontend-extensions-operations.cjs.d.ts +2 -0
- package/dist/commercetools-frontend-extensions-operations.cjs.dev.js +3273 -0
- package/dist/commercetools-frontend-extensions-operations.cjs.js +7 -0
- package/dist/commercetools-frontend-extensions-operations.cjs.prod.js +3265 -0
- package/dist/commercetools-frontend-extensions-operations.esm.js +3074 -0
- package/dist/declarations/src/@api/export-operations.d.ts +5 -0
- package/dist/declarations/src/@api/fetcher.d.ts +17 -0
- package/dist/declarations/src/@api/file-import-jobs.d.ts +7 -0
- package/dist/declarations/src/@api/file-upload.d.ts +3 -0
- package/dist/declarations/src/@api/import-containers.d.ts +35 -0
- package/dist/declarations/src/@api/import-operations.d.ts +6 -0
- package/dist/declarations/src/@api/index.d.ts +9 -0
- package/dist/declarations/src/@api/process-file.d.ts +3 -0
- package/dist/declarations/src/@api/test-fixtures.d.ts +279 -0
- package/dist/declarations/src/@api/urls.d.ts +74 -0
- package/dist/declarations/src/@components/file-drop-area/active-drag-drop-area.d.ts +10 -0
- package/dist/declarations/src/@components/file-drop-area/disabled-drop-area.d.ts +5 -0
- package/dist/declarations/src/@components/file-drop-area/drop-area-wrapper.d.ts +11 -0
- package/dist/declarations/src/@components/file-drop-area/enabled-drop-area.d.ts +7 -0
- package/dist/declarations/src/@components/file-drop-area/file-drop-area.d.ts +14 -0
- package/dist/declarations/src/@components/file-drop-area/file-dropped-area.d.ts +6 -0
- package/dist/declarations/src/@components/file-drop-area/index.d.ts +7 -0
- package/dist/declarations/src/@components/file-drop-area/styles.d.ts +9 -0
- package/dist/declarations/src/@components/icons/file-icon.d.ts +2 -0
- package/dist/declarations/src/@components/icons/index.d.ts +2 -0
- package/dist/declarations/src/@components/icons/lock-icon.d.ts +2 -0
- package/dist/declarations/src/@components/index.d.ts +6 -0
- package/dist/declarations/src/@components/info-box/index.d.ts +1 -0
- package/dist/declarations/src/@components/info-box/info-box.d.ts +7 -0
- package/dist/declarations/src/@components/upload-separator/index.d.ts +1 -0
- package/dist/declarations/src/@components/upload-separator/upload-separator.d.ts +12 -0
- package/dist/declarations/src/@components/upload-settings/index.d.ts +1 -0
- package/dist/declarations/src/@components/upload-settings/upload-settings.d.ts +11 -0
- package/dist/declarations/src/@components/uploading-modal/index.d.ts +1 -0
- package/dist/declarations/src/@components/uploading-modal/uploading-modal.d.ts +13 -0
- package/dist/declarations/src/@constants/delimiters.d.ts +8 -0
- package/dist/declarations/src/@constants/file-import-job.d.ts +1 -0
- package/dist/declarations/src/@constants/import-limits.d.ts +6 -0
- package/dist/declarations/src/@constants/import-tags.d.ts +7 -0
- package/dist/declarations/src/@constants/index.d.ts +5 -0
- package/dist/declarations/src/@constants/resource-links.d.ts +10 -0
- package/dist/declarations/src/@errors/http-error.d.ts +6 -0
- package/dist/declarations/src/@errors/index.d.ts +9 -0
- package/dist/declarations/src/@errors/invalid-response-error.d.ts +3 -0
- package/dist/declarations/src/@errors/no-resources-to-export-error.d.ts +3 -0
- package/dist/declarations/src/@errors/polling-aborted-error.d.ts +3 -0
- package/dist/declarations/src/@errors/project-key-not-available-error.d.ts +3 -0
- package/dist/declarations/src/@errors/query-predicate-error.d.ts +4 -0
- package/dist/declarations/src/@errors/unexpected-column-error.d.ts +3 -0
- package/dist/declarations/src/@errors/unexpected-operation-state-error.d.ts +4 -0
- package/dist/declarations/src/@errors/unexpected-resource-type-error.d.ts +3 -0
- package/dist/declarations/src/@hooks/index.d.ts +8 -0
- package/dist/declarations/src/@hooks/use-fetch-export-operations.d.ts +15 -0
- package/dist/declarations/src/@hooks/use-fetch-file-import-job.d.ts +17 -0
- package/dist/declarations/src/@hooks/use-fetch-import-container-details.d.ts +15 -0
- package/dist/declarations/src/@hooks/use-fetch-import-operations.d.ts +16 -0
- package/dist/declarations/src/@hooks/use-fetch-import-summaries.d.ts +20 -0
- package/dist/declarations/src/@hooks/use-file-import-job-upload.d.ts +18 -0
- package/dist/declarations/src/@hooks/use-file-upload.d.ts +28 -0
- package/dist/declarations/src/@hooks/use-import-container-upload.d.ts +19 -0
- package/dist/declarations/src/@types/api.d.ts +13 -0
- package/dist/declarations/src/@types/basic-error-data-type.d.ts +5 -0
- package/dist/declarations/src/@types/export-operation.d.ts +97 -0
- package/dist/declarations/src/@types/file-import-job.d.ts +99 -0
- package/dist/declarations/src/@types/file-upload-result.d.ts +21 -0
- package/dist/declarations/src/@types/file-upload.d.ts +63 -0
- package/dist/declarations/src/@types/import-container.d.ts +53 -0
- package/dist/declarations/src/@types/import-operation.d.ts +13 -0
- package/dist/declarations/src/@types/import-states.d.ts +9 -0
- package/dist/declarations/src/@types/import-summary.d.ts +15 -0
- package/dist/declarations/src/@types/index.d.ts +11 -0
- package/dist/declarations/src/@types/shared.d.ts +7 -0
- package/dist/declarations/src/@utils/error-mapping.d.ts +19 -0
- package/dist/declarations/src/@utils/file-import-job-helpers.d.ts +12 -0
- package/dist/declarations/src/@utils/file-upload.d.ts +54 -0
- package/dist/declarations/src/@utils/form.d.ts +1 -0
- package/dist/declarations/src/@utils/format.d.ts +5 -0
- package/dist/declarations/src/@utils/import-container.d.ts +8 -0
- package/dist/declarations/src/@utils/index.d.ts +8 -0
- package/dist/declarations/src/@utils/poll-job-until-validated.d.ts +11 -0
- package/dist/declarations/src/@utils/url.d.ts +6 -0
- package/dist/declarations/src/index.d.ts +26 -0
- package/index.js +1 -0
- package/jest.test.config.js +11 -0
- package/package.json +62 -0
- package/src/@api/export-operations.ts +26 -0
- package/src/@api/fetcher.spec.ts +51 -0
- package/src/@api/fetcher.ts +137 -0
- package/src/@api/file-import-jobs.ts +217 -0
- package/src/@api/file-upload.spec.ts +85 -0
- package/src/@api/file-upload.ts +46 -0
- package/src/@api/import-containers.ts +256 -0
- package/src/@api/import-operations.ts +33 -0
- package/src/@api/index.ts +9 -0
- package/src/@api/process-file.spec.ts +74 -0
- package/src/@api/process-file.ts +53 -0
- package/src/@api/test-fixtures.ts +894 -0
- package/src/@api/urls.ts +194 -0
- package/src/@components/file-drop-area/active-drag-drop-area.tsx +33 -0
- package/src/@components/file-drop-area/disabled-drop-area.tsx +17 -0
- package/src/@components/file-drop-area/drop-area-wrapper.tsx +38 -0
- package/src/@components/file-drop-area/enabled-drop-area.tsx +27 -0
- package/src/@components/file-drop-area/file-drop-area.tsx +74 -0
- package/src/@components/file-drop-area/file-dropped-area.tsx +29 -0
- package/src/@components/file-drop-area/index.ts +7 -0
- package/src/@components/file-drop-area/styles.ts +67 -0
- package/src/@components/icons/file-icon.tsx +30 -0
- package/src/@components/icons/index.ts +2 -0
- package/src/@components/icons/lock-icon.tsx +34 -0
- package/src/@components/index.ts +6 -0
- package/src/@components/info-box/index.ts +1 -0
- package/src/@components/info-box/info-box.tsx +23 -0
- package/src/@components/upload-separator/index.ts +1 -0
- package/src/@components/upload-separator/upload-separator.tsx +61 -0
- package/src/@components/upload-settings/index.ts +1 -0
- package/src/@components/upload-settings/upload-settings.tsx +36 -0
- package/src/@components/uploading-modal/index.ts +1 -0
- package/src/@components/uploading-modal/uploading-modal.tsx +66 -0
- package/src/@constants/delimiters.ts +14 -0
- package/src/@constants/file-import-job.ts +1 -0
- package/src/@constants/import-limits.ts +13 -0
- package/src/@constants/import-tags.ts +9 -0
- package/src/@constants/index.ts +5 -0
- package/src/@constants/resource-links.ts +61 -0
- package/src/@errors/http-error.ts +17 -0
- package/src/@errors/index.ts +9 -0
- package/src/@errors/invalid-response-error.ts +6 -0
- package/src/@errors/no-resources-to-export-error.ts +6 -0
- package/src/@errors/polling-aborted-error.ts +6 -0
- package/src/@errors/project-key-not-available-error.ts +6 -0
- package/src/@errors/query-predicate-error.ts +10 -0
- package/src/@errors/unexpected-column-error.ts +6 -0
- package/src/@errors/unexpected-operation-state-error.ts +8 -0
- package/src/@errors/unexpected-resource-type-error.ts +6 -0
- package/src/@hooks/index.ts +8 -0
- package/src/@hooks/use-fetch-export-operations.ts +34 -0
- package/src/@hooks/use-fetch-file-import-job.spec.ts +131 -0
- package/src/@hooks/use-fetch-file-import-job.ts +38 -0
- package/src/@hooks/use-fetch-import-container-details.ts +31 -0
- package/src/@hooks/use-fetch-import-operations.ts +42 -0
- package/src/@hooks/use-fetch-import-summaries.ts +47 -0
- package/src/@hooks/use-fetch.spec.ts +68 -0
- package/src/@hooks/use-fetch.ts +76 -0
- package/src/@hooks/use-file-import-job-upload.spec.ts +273 -0
- package/src/@hooks/use-file-import-job-upload.ts +101 -0
- package/src/@hooks/use-file-upload.ts +223 -0
- package/src/@hooks/use-import-container-upload.spec.ts +297 -0
- package/src/@hooks/use-import-container-upload.ts +130 -0
- package/src/@types/api.ts +14 -0
- package/src/@types/basic-error-data-type.ts +5 -0
- package/src/@types/export-operation.ts +147 -0
- package/src/@types/file-import-job.ts +165 -0
- package/src/@types/file-upload-result.ts +23 -0
- package/src/@types/file-upload.ts +81 -0
- package/src/@types/import-container.ts +104 -0
- package/src/@types/import-operation.ts +31 -0
- package/src/@types/import-states.ts +9 -0
- package/src/@types/import-summary.ts +22 -0
- package/src/@types/index.ts +11 -0
- package/src/@types/shared.ts +52 -0
- package/src/@utils/error-mapping.spec.ts +126 -0
- package/src/@utils/error-mapping.ts +40 -0
- package/src/@utils/file-import-job-helpers.spec.ts +147 -0
- package/src/@utils/file-import-job-helpers.ts +47 -0
- package/src/@utils/file-upload.spec.ts +151 -0
- package/src/@utils/file-upload.ts +189 -0
- package/src/@utils/form.ts +20 -0
- package/src/@utils/format.spec.ts +62 -0
- package/src/@utils/format.ts +53 -0
- package/src/@utils/import-container.spec.ts +26 -0
- package/src/@utils/import-container.ts +34 -0
- package/src/@utils/index.ts +8 -0
- package/src/@utils/poll-job-until-validated.ts +76 -0
- package/src/@utils/url.spec.ts +75 -0
- package/src/@utils/url.ts +18 -0
- package/src/index.ts +27 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
// TODO: Remove rowsCount and columnsCount once we remove the old flow
|
|
14
|
+
rowsCount: 0,
|
|
15
|
+
columnsCount: 0,
|
|
16
|
+
//
|
|
17
|
+
fields: ['id', 'name', 'description'],
|
|
18
|
+
ignoredFields: ['id', 'name'],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type ResourceTypeEntry = {
|
|
22
|
+
name: string
|
|
23
|
+
apiPath: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const resourceTypes: Array<ResourceTypeEntry> = [
|
|
27
|
+
{
|
|
28
|
+
name: 'category',
|
|
29
|
+
apiPath: 'categories',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'product',
|
|
33
|
+
apiPath: 'products',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'inventory',
|
|
37
|
+
apiPath: 'inventories',
|
|
38
|
+
},
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
const mockServer = setupServer(
|
|
42
|
+
...resourceTypes.map(({ apiPath }) =>
|
|
43
|
+
rest.post(
|
|
44
|
+
`http://localhost:8080/proxy/import/test-with-big-data-key/${apiPath}/import-containers/container-key/file-upload`,
|
|
45
|
+
(_, res, ctx) => res(ctx.json(validFileUploadResponse))
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
mockServer.resetHandlers()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
beforeAll(() => {
|
|
55
|
+
mockServer.listen()
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
afterAll(() => {
|
|
59
|
+
mockServer.close()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe('uploadFileForImport', () => {
|
|
63
|
+
const projectKey = 'test-with-big-data-key'
|
|
64
|
+
const containerKey = 'container-key'
|
|
65
|
+
const file: File = new File([], 'file.csv')
|
|
66
|
+
|
|
67
|
+
it.each([['category'], ['product'], ['inventory']])(
|
|
68
|
+
'should post the file to the correct endpoint for the resource type %s',
|
|
69
|
+
async (resourceType) => {
|
|
70
|
+
const onSuccess = jest.fn()
|
|
71
|
+
uploadFileForImport({
|
|
72
|
+
projectKey,
|
|
73
|
+
importContainerKey: containerKey,
|
|
74
|
+
resourceType,
|
|
75
|
+
file,
|
|
76
|
+
onSuccess,
|
|
77
|
+
onProgress: jest.fn(),
|
|
78
|
+
onError: jest.fn(),
|
|
79
|
+
})
|
|
80
|
+
await waitFor(() =>
|
|
81
|
+
expect(onSuccess).toHaveBeenCalledWith(validFileUploadResponse)
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
})
|
|
@@ -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,9 @@
|
|
|
1
|
+
export * from './fetcher'
|
|
2
|
+
export * from './file-upload'
|
|
3
|
+
export * from './file-import-jobs'
|
|
4
|
+
export * from './import-containers'
|
|
5
|
+
export * from './import-operations'
|
|
6
|
+
export * from './export-operations'
|
|
7
|
+
export * from './process-file'
|
|
8
|
+
export * from './urls'
|
|
9
|
+
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
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants'
|
|
2
|
+
import type {
|
|
3
|
+
ProcessUploadedFileRequestParameters,
|
|
4
|
+
ProcessFileResponse,
|
|
5
|
+
} from '../@types'
|
|
6
|
+
import { hasRequiredFields } from '../@types'
|
|
7
|
+
import { fetcher } from './fetcher'
|
|
8
|
+
import { getProccessFileURL, getImportContainerTasksURL } from './urls'
|
|
9
|
+
|
|
10
|
+
export async function processUploadedFile({
|
|
11
|
+
projectKey,
|
|
12
|
+
importContainerKey,
|
|
13
|
+
resourceType,
|
|
14
|
+
action,
|
|
15
|
+
}: ProcessUploadedFileRequestParameters): Promise<ProcessFileResponse> {
|
|
16
|
+
// For delete operations with action (like 'delete') -> use different URL and payload structure
|
|
17
|
+
const uri = action
|
|
18
|
+
? getImportContainerTasksURL({ projectKey, importContainerKey })
|
|
19
|
+
: getProccessFileURL({ projectKey, resourceType, importContainerKey })
|
|
20
|
+
|
|
21
|
+
const payload = action
|
|
22
|
+
? {
|
|
23
|
+
task: 'process-file',
|
|
24
|
+
parameter: {
|
|
25
|
+
resourceType,
|
|
26
|
+
action,
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
: {}
|
|
30
|
+
|
|
31
|
+
const response = await fetcher<ProcessFileResponse>({
|
|
32
|
+
url: uri,
|
|
33
|
+
payload: JSON.stringify(payload),
|
|
34
|
+
config: {
|
|
35
|
+
proxy: MC_API_PROXY_TARGETS.IMPORT,
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
accept: 'application/json',
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
assertProcessFileResponse(response)
|
|
44
|
+
return response
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function assertProcessFileResponse(
|
|
48
|
+
maybeProcessFileResponse: unknown
|
|
49
|
+
): asserts maybeProcessFileResponse is ProcessFileResponse {
|
|
50
|
+
const requiredFields = ['message']
|
|
51
|
+
if (hasRequiredFields(maybeProcessFileResponse, requiredFields)) return
|
|
52
|
+
throw new Error('Invalid response')
|
|
53
|
+
}
|