@commercetools-frontend-extensions/operations 3.4.0 → 3.6.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.
- package/CHANGELOG.md +14 -0
- package/README.md +2 -0
- package/dist/commercetools-frontend-extensions-operations.cjs.dev.js +153 -42
- package/dist/commercetools-frontend-extensions-operations.cjs.prod.js +153 -42
- package/dist/commercetools-frontend-extensions-operations.esm.js +144 -43
- package/dist/declarations/src/@errors/base.d.ts +19 -0
- package/dist/declarations/src/@errors/guards.d.ts +12 -0
- package/dist/declarations/src/@errors/http-error.d.ts +4 -1
- package/dist/declarations/src/@errors/index.d.ts +3 -0
- package/dist/declarations/src/@errors/invalid-response-error.d.ts +4 -1
- package/dist/declarations/src/@errors/no-resources-to-export-error.d.ts +4 -1
- package/dist/declarations/src/@errors/polling-aborted-error.d.ts +4 -1
- package/dist/declarations/src/@errors/polling-timeout-error.d.ts +8 -0
- package/dist/declarations/src/@errors/project-key-not-available-error.d.ts +4 -1
- package/dist/declarations/src/@errors/query-predicate-error.d.ts +5 -2
- package/dist/declarations/src/@errors/unexpected-column-error.d.ts +5 -1
- package/dist/declarations/src/@errors/unexpected-operation-state-error.d.ts +5 -1
- package/dist/declarations/src/@errors/unexpected-resource-type-error.d.ts +5 -1
- package/dist/declarations/src/@hooks/use-file-import-job-upload.d.ts +1 -0
- package/dist/declarations/src/@hooks/use-file-upload.d.ts +1 -0
- package/package.json +1 -1
- package/src/@errors/base.ts +30 -0
- package/src/@errors/guards.ts +43 -0
- package/src/@errors/http-error.ts +5 -1
- package/src/@errors/index.ts +3 -0
- package/src/@errors/invalid-response-error.ts +6 -1
- package/src/@errors/no-resources-to-export-error.ts +6 -1
- package/src/@errors/polling-aborted-error.ts +6 -1
- package/src/@errors/polling-timeout-error.ts +17 -0
- package/src/@errors/project-key-not-available-error.ts +6 -1
- package/src/@errors/query-predicate-error.ts +6 -2
- package/src/@errors/unexpected-column-error.ts +8 -1
- package/src/@errors/unexpected-operation-state-error.ts +7 -1
- package/src/@errors/unexpected-resource-type-error.ts +8 -1
- package/src/@hooks/use-file-import-job-upload.spec.ts +49 -0
- package/src/@hooks/use-file-import-job-upload.ts +5 -1
- package/src/@hooks/use-file-upload.ts +2 -0
- package/src/@utils/poll-job-until-processing.ts +3 -6
- package/src/@utils/poll-job-until-validated.ts +3 -6
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class InvalidResponseError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.INVALID_RESPONSE
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
|
|
2
7
|
constructor(message: string) {
|
|
3
8
|
super(message)
|
|
4
9
|
this.name = 'InvalidResponseError'
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class NoResourcesToExportError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.NO_RESOURCES_TO_EXPORT
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
|
|
2
7
|
constructor(message: string = 'There are no resources to export.') {
|
|
3
8
|
super(message)
|
|
4
9
|
this.name = 'NoResourcesToExportError'
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class PollingAbortedError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.POLLING_ABORTED
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
|
|
2
7
|
constructor() {
|
|
3
8
|
super('Polling was aborted')
|
|
4
9
|
this.name = 'PollingAbortedError'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class PollingTimeoutError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.POLLING_TIMEOUT
|
|
5
|
+
readonly isRetryable = true
|
|
6
|
+
readonly maxAttempts: number
|
|
7
|
+
readonly totalTimeSeconds: number
|
|
8
|
+
|
|
9
|
+
constructor(maxAttempts: number, totalTimeSeconds: number) {
|
|
10
|
+
super(
|
|
11
|
+
`Polling timeout after ${maxAttempts} attempts (${totalTimeSeconds}s)`
|
|
12
|
+
)
|
|
13
|
+
this.name = 'PollingTimeoutError'
|
|
14
|
+
this.maxAttempts = maxAttempts
|
|
15
|
+
this.totalTimeSeconds = totalTimeSeconds
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class ProjectKeyNotAvailableError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.PROJECT_KEY_NOT_AVAILABLE
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
|
|
2
7
|
constructor(message: string = 'Project key is not available') {
|
|
3
8
|
super(message)
|
|
4
9
|
this.name = 'ProjectKeyNotAvailableError'
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class QueryPredicateError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.QUERY_PREDICATE_ERROR
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
readonly field = 'queryPredicate'
|
|
3
7
|
|
|
4
8
|
constructor(
|
|
5
9
|
message: string = 'There is an error with the query predicate. Make sure the syntax is correct.'
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class UnexpectedColumnError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.UNEXPECTED_COLUMN
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
readonly columnName: string
|
|
7
|
+
|
|
2
8
|
constructor(columnName: string) {
|
|
3
9
|
super(`Unexpected column "${columnName}"`)
|
|
4
10
|
this.name = 'UnexpectedColumnError'
|
|
11
|
+
this.columnName = columnName
|
|
5
12
|
}
|
|
6
13
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { ProcessingState } from '@commercetools/importapi-sdk'
|
|
2
|
+
import { ErrorCode, OperationsError } from './base'
|
|
3
|
+
|
|
4
|
+
export class UnexpectedOperationStateError extends OperationsError {
|
|
5
|
+
readonly code = ErrorCode.UNEXPECTED_OPERATION_STATE
|
|
6
|
+
readonly isRetryable = false
|
|
7
|
+
readonly state: ProcessingState
|
|
2
8
|
|
|
3
|
-
export class UnexpectedOperationStateError extends Error {
|
|
4
9
|
constructor(state: ProcessingState) {
|
|
5
10
|
super(`Unexpected operation state "${state}"`)
|
|
6
11
|
this.name = 'UnexpectedOperationStateError'
|
|
12
|
+
this.state = state
|
|
7
13
|
}
|
|
8
14
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { ErrorCode, OperationsError } from './base'
|
|
2
|
+
|
|
3
|
+
export class UnexpectedResourceTypeError extends OperationsError {
|
|
4
|
+
readonly code = ErrorCode.UNEXPECTED_RESOURCE_TYPE
|
|
5
|
+
readonly isRetryable = false
|
|
6
|
+
readonly resourceType: string
|
|
7
|
+
|
|
2
8
|
constructor(resourceType: string) {
|
|
3
9
|
super(`Unexpected resource type "${resourceType}"`)
|
|
4
10
|
this.name = 'UnexpectedResourceTypeError'
|
|
11
|
+
this.resourceType = resourceType
|
|
5
12
|
}
|
|
6
13
|
}
|
|
@@ -262,6 +262,55 @@ describe('useFileImportJobUpload', () => {
|
|
|
262
262
|
expect(result.current.progress).toBe(100)
|
|
263
263
|
})
|
|
264
264
|
|
|
265
|
+
it('should use explicit fileType override instead of deriving from resourceType', async () => {
|
|
266
|
+
const onSuccess = jest.fn()
|
|
267
|
+
|
|
268
|
+
const { result } = renderHook(() => useFileImportJobUpload({ projectKey }))
|
|
269
|
+
|
|
270
|
+
await act(async () => {
|
|
271
|
+
await result.current.upload({
|
|
272
|
+
file: mockFile,
|
|
273
|
+
resourceType: 'product',
|
|
274
|
+
fileType: 'json',
|
|
275
|
+
onSuccess,
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
expect(mockCreateFileImportJob).toHaveBeenCalledWith(
|
|
280
|
+
expect.objectContaining({
|
|
281
|
+
payload: expect.objectContaining({
|
|
282
|
+
fileType: 'json',
|
|
283
|
+
}),
|
|
284
|
+
})
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
expect(mockGetFileImportJobFileType).not.toHaveBeenCalled()
|
|
288
|
+
expect(onSuccess).toHaveBeenCalledWith('job-123', importContainerKey)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('should fall back to derived fileType when fileType override is not provided', async () => {
|
|
292
|
+
const onSuccess = jest.fn()
|
|
293
|
+
|
|
294
|
+
const { result } = renderHook(() => useFileImportJobUpload({ projectKey }))
|
|
295
|
+
|
|
296
|
+
await act(async () => {
|
|
297
|
+
await result.current.upload({
|
|
298
|
+
file: mockFile,
|
|
299
|
+
resourceType: 'product',
|
|
300
|
+
onSuccess,
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
expect(mockGetFileImportJobFileType).toHaveBeenCalledWith('product')
|
|
305
|
+
expect(mockCreateFileImportJob).toHaveBeenCalledWith(
|
|
306
|
+
expect.objectContaining({
|
|
307
|
+
payload: expect.objectContaining({
|
|
308
|
+
fileType: 'csv',
|
|
309
|
+
}),
|
|
310
|
+
})
|
|
311
|
+
)
|
|
312
|
+
})
|
|
313
|
+
|
|
265
314
|
it('should work without optional settings', async () => {
|
|
266
315
|
const onSuccess = jest.fn()
|
|
267
316
|
|
|
@@ -16,6 +16,7 @@ import type { ExtendedImportContainerDraft } from '../@types'
|
|
|
16
16
|
export type UseFileImportJobUploadConfig = {
|
|
17
17
|
file: File
|
|
18
18
|
resourceType: ResourceTypeId
|
|
19
|
+
fileType?: 'csv' | 'json'
|
|
19
20
|
settings?: ExtendedImportContainerDraft['settings']
|
|
20
21
|
autoProcess?: boolean
|
|
21
22
|
operationType?: 'delete'
|
|
@@ -57,12 +58,15 @@ export const useFileImportJobUpload = ({
|
|
|
57
58
|
projectKey,
|
|
58
59
|
})
|
|
59
60
|
|
|
61
|
+
const fileType =
|
|
62
|
+
config.fileType ?? getFileImportJobFileType(config.resourceType)
|
|
63
|
+
|
|
60
64
|
const jobResponse = await createFileImportJob({
|
|
61
65
|
projectKey,
|
|
62
66
|
resourceType: config.resourceType,
|
|
63
67
|
importContainerKey,
|
|
64
68
|
payload: {
|
|
65
|
-
fileType
|
|
69
|
+
fileType,
|
|
66
70
|
fileName: config.file.name,
|
|
67
71
|
file: config.file,
|
|
68
72
|
},
|
|
@@ -19,6 +19,7 @@ export type ValidationProgress = {
|
|
|
19
19
|
export type FileUploadConfig = {
|
|
20
20
|
file: File
|
|
21
21
|
resourceType: ResourceTypeId
|
|
22
|
+
fileType?: 'csv' | 'json'
|
|
22
23
|
settings?: ExtendedImportContainerDraft['settings']
|
|
23
24
|
autoProcess?: boolean
|
|
24
25
|
skipValidationPolling?: boolean
|
|
@@ -85,6 +86,7 @@ export const useFileUpload = ({
|
|
|
85
86
|
await jobUpload.upload({
|
|
86
87
|
file: config.file,
|
|
87
88
|
resourceType: config.resourceType,
|
|
89
|
+
fileType: config.fileType,
|
|
88
90
|
settings: config.settings,
|
|
89
91
|
autoProcess: config.autoProcess,
|
|
90
92
|
operationType: config.operationType,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getFileImportJob } from '../@api'
|
|
2
|
-
import { PollingAbortedError } from '../@errors'
|
|
2
|
+
import { PollingAbortedError, PollingTimeoutError } from '../@errors'
|
|
3
3
|
import type { FileImportJob } from '../@types'
|
|
4
4
|
import { hasImportJobStartedProcessing } from './file-import-job-helpers'
|
|
5
5
|
|
|
@@ -64,9 +64,6 @@ export const pollJobUntilProcessing = async ({
|
|
|
64
64
|
attempts++
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
(maxAttempts * pollingInterval) / 1000
|
|
70
|
-
}s)`
|
|
71
|
-
)
|
|
67
|
+
const totalTimeSeconds = (maxAttempts * pollingInterval) / 1000
|
|
68
|
+
throw new PollingTimeoutError(maxAttempts, totalTimeSeconds)
|
|
72
69
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getFileImportJob } from '../@api'
|
|
2
|
-
import { PollingAbortedError } from '../@errors'
|
|
2
|
+
import { PollingAbortedError, PollingTimeoutError } from '../@errors'
|
|
3
3
|
import type { FileImportJob } from '../@types'
|
|
4
4
|
import { isImportJobTerminal } from './file-import-job-helpers'
|
|
5
5
|
|
|
@@ -68,9 +68,6 @@ export const pollJobUntilValidated = async ({
|
|
|
68
68
|
attempts++
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
(maxAttempts * pollingInterval) / 1000
|
|
74
|
-
}s)`
|
|
75
|
-
)
|
|
71
|
+
const totalTimeSeconds = (maxAttempts * pollingInterval) / 1000
|
|
72
|
+
throw new PollingTimeoutError(maxAttempts, totalTimeSeconds)
|
|
76
73
|
}
|