@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.
Files changed (180) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +178 -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 +3273 -0
  6. package/dist/commercetools-frontend-extensions-operations.cjs.js +7 -0
  7. package/dist/commercetools-frontend-extensions-operations.cjs.prod.js +3265 -0
  8. package/dist/commercetools-frontend-extensions-operations.esm.js +3074 -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-import-jobs.d.ts +7 -0
  12. package/dist/declarations/src/@api/file-upload.d.ts +3 -0
  13. package/dist/declarations/src/@api/import-containers.d.ts +35 -0
  14. package/dist/declarations/src/@api/import-operations.d.ts +6 -0
  15. package/dist/declarations/src/@api/index.d.ts +9 -0
  16. package/dist/declarations/src/@api/process-file.d.ts +3 -0
  17. package/dist/declarations/src/@api/test-fixtures.d.ts +279 -0
  18. package/dist/declarations/src/@api/urls.d.ts +74 -0
  19. package/dist/declarations/src/@components/file-drop-area/active-drag-drop-area.d.ts +10 -0
  20. package/dist/declarations/src/@components/file-drop-area/disabled-drop-area.d.ts +5 -0
  21. package/dist/declarations/src/@components/file-drop-area/drop-area-wrapper.d.ts +11 -0
  22. package/dist/declarations/src/@components/file-drop-area/enabled-drop-area.d.ts +7 -0
  23. package/dist/declarations/src/@components/file-drop-area/file-drop-area.d.ts +14 -0
  24. package/dist/declarations/src/@components/file-drop-area/file-dropped-area.d.ts +6 -0
  25. package/dist/declarations/src/@components/file-drop-area/index.d.ts +7 -0
  26. package/dist/declarations/src/@components/file-drop-area/styles.d.ts +9 -0
  27. package/dist/declarations/src/@components/icons/file-icon.d.ts +2 -0
  28. package/dist/declarations/src/@components/icons/index.d.ts +2 -0
  29. package/dist/declarations/src/@components/icons/lock-icon.d.ts +2 -0
  30. package/dist/declarations/src/@components/index.d.ts +6 -0
  31. package/dist/declarations/src/@components/info-box/index.d.ts +1 -0
  32. package/dist/declarations/src/@components/info-box/info-box.d.ts +7 -0
  33. package/dist/declarations/src/@components/upload-separator/index.d.ts +1 -0
  34. package/dist/declarations/src/@components/upload-separator/upload-separator.d.ts +12 -0
  35. package/dist/declarations/src/@components/upload-settings/index.d.ts +1 -0
  36. package/dist/declarations/src/@components/upload-settings/upload-settings.d.ts +11 -0
  37. package/dist/declarations/src/@components/uploading-modal/index.d.ts +1 -0
  38. package/dist/declarations/src/@components/uploading-modal/uploading-modal.d.ts +13 -0
  39. package/dist/declarations/src/@constants/delimiters.d.ts +8 -0
  40. package/dist/declarations/src/@constants/file-import-job.d.ts +1 -0
  41. package/dist/declarations/src/@constants/import-limits.d.ts +6 -0
  42. package/dist/declarations/src/@constants/import-tags.d.ts +7 -0
  43. package/dist/declarations/src/@constants/index.d.ts +5 -0
  44. package/dist/declarations/src/@constants/resource-links.d.ts +10 -0
  45. package/dist/declarations/src/@errors/http-error.d.ts +6 -0
  46. package/dist/declarations/src/@errors/index.d.ts +9 -0
  47. package/dist/declarations/src/@errors/invalid-response-error.d.ts +3 -0
  48. package/dist/declarations/src/@errors/no-resources-to-export-error.d.ts +3 -0
  49. package/dist/declarations/src/@errors/polling-aborted-error.d.ts +3 -0
  50. package/dist/declarations/src/@errors/project-key-not-available-error.d.ts +3 -0
  51. package/dist/declarations/src/@errors/query-predicate-error.d.ts +4 -0
  52. package/dist/declarations/src/@errors/unexpected-column-error.d.ts +3 -0
  53. package/dist/declarations/src/@errors/unexpected-operation-state-error.d.ts +4 -0
  54. package/dist/declarations/src/@errors/unexpected-resource-type-error.d.ts +3 -0
  55. package/dist/declarations/src/@hooks/index.d.ts +8 -0
  56. package/dist/declarations/src/@hooks/use-fetch-export-operations.d.ts +15 -0
  57. package/dist/declarations/src/@hooks/use-fetch-file-import-job.d.ts +17 -0
  58. package/dist/declarations/src/@hooks/use-fetch-import-container-details.d.ts +15 -0
  59. package/dist/declarations/src/@hooks/use-fetch-import-operations.d.ts +16 -0
  60. package/dist/declarations/src/@hooks/use-fetch-import-summaries.d.ts +20 -0
  61. package/dist/declarations/src/@hooks/use-file-import-job-upload.d.ts +18 -0
  62. package/dist/declarations/src/@hooks/use-file-upload.d.ts +28 -0
  63. package/dist/declarations/src/@hooks/use-import-container-upload.d.ts +19 -0
  64. package/dist/declarations/src/@types/api.d.ts +13 -0
  65. package/dist/declarations/src/@types/basic-error-data-type.d.ts +5 -0
  66. package/dist/declarations/src/@types/export-operation.d.ts +97 -0
  67. package/dist/declarations/src/@types/file-import-job.d.ts +99 -0
  68. package/dist/declarations/src/@types/file-upload-result.d.ts +21 -0
  69. package/dist/declarations/src/@types/file-upload.d.ts +63 -0
  70. package/dist/declarations/src/@types/import-container.d.ts +53 -0
  71. package/dist/declarations/src/@types/import-operation.d.ts +13 -0
  72. package/dist/declarations/src/@types/import-states.d.ts +9 -0
  73. package/dist/declarations/src/@types/import-summary.d.ts +15 -0
  74. package/dist/declarations/src/@types/index.d.ts +11 -0
  75. package/dist/declarations/src/@types/shared.d.ts +7 -0
  76. package/dist/declarations/src/@utils/error-mapping.d.ts +19 -0
  77. package/dist/declarations/src/@utils/file-import-job-helpers.d.ts +12 -0
  78. package/dist/declarations/src/@utils/file-upload.d.ts +54 -0
  79. package/dist/declarations/src/@utils/form.d.ts +1 -0
  80. package/dist/declarations/src/@utils/format.d.ts +5 -0
  81. package/dist/declarations/src/@utils/import-container.d.ts +8 -0
  82. package/dist/declarations/src/@utils/index.d.ts +8 -0
  83. package/dist/declarations/src/@utils/poll-job-until-validated.d.ts +11 -0
  84. package/dist/declarations/src/@utils/url.d.ts +6 -0
  85. package/dist/declarations/src/index.d.ts +26 -0
  86. package/index.js +1 -0
  87. package/jest.test.config.js +11 -0
  88. package/package.json +62 -0
  89. package/src/@api/export-operations.ts +26 -0
  90. package/src/@api/fetcher.spec.ts +51 -0
  91. package/src/@api/fetcher.ts +137 -0
  92. package/src/@api/file-import-jobs.ts +217 -0
  93. package/src/@api/file-upload.spec.ts +85 -0
  94. package/src/@api/file-upload.ts +46 -0
  95. package/src/@api/import-containers.ts +256 -0
  96. package/src/@api/import-operations.ts +33 -0
  97. package/src/@api/index.ts +9 -0
  98. package/src/@api/process-file.spec.ts +74 -0
  99. package/src/@api/process-file.ts +53 -0
  100. package/src/@api/test-fixtures.ts +894 -0
  101. package/src/@api/urls.ts +194 -0
  102. package/src/@components/file-drop-area/active-drag-drop-area.tsx +33 -0
  103. package/src/@components/file-drop-area/disabled-drop-area.tsx +17 -0
  104. package/src/@components/file-drop-area/drop-area-wrapper.tsx +38 -0
  105. package/src/@components/file-drop-area/enabled-drop-area.tsx +27 -0
  106. package/src/@components/file-drop-area/file-drop-area.tsx +74 -0
  107. package/src/@components/file-drop-area/file-dropped-area.tsx +29 -0
  108. package/src/@components/file-drop-area/index.ts +7 -0
  109. package/src/@components/file-drop-area/styles.ts +67 -0
  110. package/src/@components/icons/file-icon.tsx +30 -0
  111. package/src/@components/icons/index.ts +2 -0
  112. package/src/@components/icons/lock-icon.tsx +34 -0
  113. package/src/@components/index.ts +6 -0
  114. package/src/@components/info-box/index.ts +1 -0
  115. package/src/@components/info-box/info-box.tsx +23 -0
  116. package/src/@components/upload-separator/index.ts +1 -0
  117. package/src/@components/upload-separator/upload-separator.tsx +61 -0
  118. package/src/@components/upload-settings/index.ts +1 -0
  119. package/src/@components/upload-settings/upload-settings.tsx +36 -0
  120. package/src/@components/uploading-modal/index.ts +1 -0
  121. package/src/@components/uploading-modal/uploading-modal.tsx +66 -0
  122. package/src/@constants/delimiters.ts +14 -0
  123. package/src/@constants/file-import-job.ts +1 -0
  124. package/src/@constants/import-limits.ts +13 -0
  125. package/src/@constants/import-tags.ts +9 -0
  126. package/src/@constants/index.ts +5 -0
  127. package/src/@constants/resource-links.ts +61 -0
  128. package/src/@errors/http-error.ts +17 -0
  129. package/src/@errors/index.ts +9 -0
  130. package/src/@errors/invalid-response-error.ts +6 -0
  131. package/src/@errors/no-resources-to-export-error.ts +6 -0
  132. package/src/@errors/polling-aborted-error.ts +6 -0
  133. package/src/@errors/project-key-not-available-error.ts +6 -0
  134. package/src/@errors/query-predicate-error.ts +10 -0
  135. package/src/@errors/unexpected-column-error.ts +6 -0
  136. package/src/@errors/unexpected-operation-state-error.ts +8 -0
  137. package/src/@errors/unexpected-resource-type-error.ts +6 -0
  138. package/src/@hooks/index.ts +8 -0
  139. package/src/@hooks/use-fetch-export-operations.ts +34 -0
  140. package/src/@hooks/use-fetch-file-import-job.spec.ts +131 -0
  141. package/src/@hooks/use-fetch-file-import-job.ts +38 -0
  142. package/src/@hooks/use-fetch-import-container-details.ts +31 -0
  143. package/src/@hooks/use-fetch-import-operations.ts +42 -0
  144. package/src/@hooks/use-fetch-import-summaries.ts +47 -0
  145. package/src/@hooks/use-fetch.spec.ts +68 -0
  146. package/src/@hooks/use-fetch.ts +76 -0
  147. package/src/@hooks/use-file-import-job-upload.spec.ts +273 -0
  148. package/src/@hooks/use-file-import-job-upload.ts +101 -0
  149. package/src/@hooks/use-file-upload.ts +223 -0
  150. package/src/@hooks/use-import-container-upload.spec.ts +297 -0
  151. package/src/@hooks/use-import-container-upload.ts +130 -0
  152. package/src/@types/api.ts +14 -0
  153. package/src/@types/basic-error-data-type.ts +5 -0
  154. package/src/@types/export-operation.ts +147 -0
  155. package/src/@types/file-import-job.ts +165 -0
  156. package/src/@types/file-upload-result.ts +23 -0
  157. package/src/@types/file-upload.ts +81 -0
  158. package/src/@types/import-container.ts +104 -0
  159. package/src/@types/import-operation.ts +31 -0
  160. package/src/@types/import-states.ts +9 -0
  161. package/src/@types/import-summary.ts +22 -0
  162. package/src/@types/index.ts +11 -0
  163. package/src/@types/shared.ts +52 -0
  164. package/src/@utils/error-mapping.spec.ts +126 -0
  165. package/src/@utils/error-mapping.ts +40 -0
  166. package/src/@utils/file-import-job-helpers.spec.ts +147 -0
  167. package/src/@utils/file-import-job-helpers.ts +47 -0
  168. package/src/@utils/file-upload.spec.ts +151 -0
  169. package/src/@utils/file-upload.ts +189 -0
  170. package/src/@utils/form.ts +20 -0
  171. package/src/@utils/format.spec.ts +62 -0
  172. package/src/@utils/format.ts +53 -0
  173. package/src/@utils/import-container.spec.ts +26 -0
  174. package/src/@utils/import-container.ts +34 -0
  175. package/src/@utils/index.ts +8 -0
  176. package/src/@utils/poll-job-until-validated.ts +76 -0
  177. package/src/@utils/url.spec.ts +75 -0
  178. package/src/@utils/url.ts +18 -0
  179. package/src/index.ts +27 -0
  180. package/tsconfig.json +9 -0
@@ -0,0 +1,36 @@
1
+ import type { ResourceTypeId } from '@commercetools/importapi-sdk'
2
+ import { CheckboxInput } from '@commercetools-frontend/ui-kit'
3
+
4
+ type UploadSettingsProps = {
5
+ resourceType?: ResourceTypeId
6
+ isPublishChecked?: boolean
7
+ onPublishChange: (checked: boolean) => void
8
+ publishProductsLabel: string
9
+ dataTrackingTarget: string
10
+ canPublishProducts?: boolean
11
+ }
12
+
13
+ export const UploadSettings = ({
14
+ resourceType,
15
+ isPublishChecked,
16
+ onPublishChange,
17
+ publishProductsLabel,
18
+ dataTrackingTarget,
19
+ canPublishProducts,
20
+ }: UploadSettingsProps) => {
21
+ if (resourceType !== 'product' || !canPublishProducts) {
22
+ return null
23
+ }
24
+
25
+ return (
26
+ <CheckboxInput
27
+ data-tracking-target={dataTrackingTarget}
28
+ isChecked={isPublishChecked}
29
+ onChange={(e) => {
30
+ onPublishChange(e.target.checked)
31
+ }}
32
+ >
33
+ {publishProductsLabel}
34
+ </CheckboxInput>
35
+ )
36
+ }
@@ -0,0 +1 @@
1
+ export * from './uploading-modal'
@@ -0,0 +1,66 @@
1
+ import { InfoDialog } from '@commercetools-frontend/application-components'
2
+ import {
3
+ Constraints,
4
+ ProgressBar,
5
+ SecondaryButton,
6
+ Spacings,
7
+ Text,
8
+ } from '@commercetools-frontend/ui-kit'
9
+ import { convertFileSizeToKB } from '../../@utils'
10
+ import { FileIcon } from '../icons/file-icon'
11
+
12
+ type UploadingModalProps = {
13
+ isOpen: boolean
14
+ title: string
15
+ fileName: string
16
+ fileSize: number
17
+ progress?: number
18
+ cancelLabel: string
19
+ onCancel: () => void
20
+ onClose: () => void
21
+ statusMessage?: string
22
+ }
23
+
24
+ export const UploadingModal = ({
25
+ isOpen,
26
+ title,
27
+ fileName,
28
+ fileSize,
29
+ progress,
30
+ cancelLabel,
31
+ onCancel,
32
+ onClose,
33
+ statusMessage,
34
+ }: UploadingModalProps) => {
35
+ return (
36
+ <InfoDialog size={16} isOpen={isOpen} title={title} onClose={onClose}>
37
+ <Spacings.Stack scale="m">
38
+ <Spacings.Inline alignItems="center" justifyContent="space-between">
39
+ <Spacings.Inline alignItems="center">
40
+ <FileIcon />
41
+ <Spacings.Inline>
42
+ <Constraints.Horizontal max={10}>
43
+ <Text.Body truncate isBold>
44
+ {fileName}
45
+ </Text.Body>
46
+ </Constraints.Horizontal>
47
+ </Spacings.Inline>
48
+ <Text.Body tone="secondary">
49
+ ({convertFileSizeToKB(fileSize).toLocaleString()} KB)
50
+ </Text.Body>
51
+ </Spacings.Inline>
52
+ <SecondaryButton
53
+ tone="secondary"
54
+ size="medium"
55
+ label={cancelLabel}
56
+ onClick={onCancel}
57
+ />
58
+ </Spacings.Inline>
59
+ <ProgressBar barWidth="scale" height="10" progress={progress} />
60
+ {statusMessage && (
61
+ <Text.Detail tone="tertiary">{statusMessage}</Text.Detail>
62
+ )}
63
+ </Spacings.Stack>
64
+ </InfoDialog>
65
+ )
66
+ }
@@ -0,0 +1,14 @@
1
+ export const DELIMITERS = {
2
+ COMMA: ',',
3
+ SEMICOLON: ';',
4
+ POINT: '.',
5
+ TAB: '\t',
6
+ PIPE: '|',
7
+ }
8
+
9
+ export const COLUMN_DELIMITERS = [
10
+ DELIMITERS.COMMA,
11
+ DELIMITERS.SEMICOLON,
12
+ DELIMITERS.PIPE,
13
+ DELIMITERS.TAB,
14
+ ]
@@ -0,0 +1 @@
1
+ export const FILE_IMPORT_JOB_POLLING_INTERVAL = 2000
@@ -0,0 +1,13 @@
1
+ export const IMPORT_MAX_FILE_SIZE_MB = 200
2
+
3
+ export const IMPORT_MAX_ITEM_COUNT = 500_000
4
+
5
+ // =============================================================================
6
+ // Legacy constants (old flow) - Remove after testing and migration is complete
7
+ // =============================================================================
8
+
9
+ /** @deprecated Use IMPORT_MAX_FILE_SIZE_MB instead. Remove after migration. */
10
+ export const IMPORT_LEGACY_MAX_FILE_SIZE_MB = 35
11
+
12
+ /** @deprecated Use IMPORT_MAX_ITEM_COUNT instead. Remove after migration. */
13
+ export const IMPORT_LEGACY_MAX_ROW_COUNT = 80_000
@@ -0,0 +1,9 @@
1
+ export const IMPORT_TAG_KEYS = {
2
+ source: 'source',
3
+ }
4
+
5
+ export const IMPORT_TAG_VALUES = {
6
+ fileUpload: 'file-upload',
7
+ }
8
+
9
+ export const TAG_KEY_SOURCE_FILE_UPLOAD = `${IMPORT_TAG_KEYS.source}:${IMPORT_TAG_VALUES.fileUpload}`
@@ -0,0 +1,5 @@
1
+ export * from './delimiters'
2
+ export * from './file-import-job'
3
+ export * from './import-limits'
4
+ export * from './import-tags'
5
+ export * from './resource-links'
@@ -0,0 +1,61 @@
1
+ import type { ResourceTypeId } from '@commercetools/importapi-sdk'
2
+
3
+ /**
4
+ * CommerceTools API documentation base URL
5
+ */
6
+ export const CT_API_DOCS_URL = 'https://docs.commercetools.com/api/'
7
+
8
+ /**
9
+ * Template download links for each resource type
10
+ */
11
+ export const RESOURCE_TYPE_TEMPLATE_DOWNLOAD_LINKS: Record<
12
+ ResourceTypeId,
13
+ string
14
+ > = {
15
+ category:
16
+ 'https://docs.commercetools.com/merchant-center/downloads/category_import_template.csv',
17
+ 'custom-object':
18
+ 'https://docs.commercetools.com/merchant-center/downloads/custom_object_import_template.json',
19
+ product:
20
+ 'https://docs.commercetools.com/merchant-center/import-data#download-a-template',
21
+ 'inventory-entry':
22
+ 'https://docs.commercetools.com/merchant-center/downloads/inventory_entry_import_template.csv',
23
+ // TODO: remove `inventory` after aligning the resource type names in the Import API and Export API
24
+ inventory:
25
+ 'https://docs.commercetools.com/merchant-center/downloads/inventory_entry_import_template.csv',
26
+ 'discount-code':
27
+ 'https://docs.commercetools.com/merchant-center/downloads/discount_code_import_template.csv',
28
+ customer:
29
+ 'https://docs.commercetools.com/merchant-center/import-data#download-a-template',
30
+ order:
31
+ 'https://docs.commercetools.com/merchant-center/import-data#download-a-template',
32
+ 'product-type':
33
+ 'https://docs.commercetools.com/merchant-center/downloads/product_type_import_template.csv',
34
+ 'business-unit':
35
+ 'https://docs.commercetools.com/merchant-center/downloads/business_unit_import_template.csv',
36
+ }
37
+
38
+ export const RESOURCE_TYPE_DOCUMENTATION_LINKS: Record<ResourceTypeId, string> =
39
+ {
40
+ category:
41
+ 'https://docs.commercetools.com/merchant-center/import-categories#supported-headers-and-values',
42
+ 'custom-object':
43
+ 'https://docs.commercetools.com/merchant-center/import-custom-objects#supported-fields-and-values',
44
+ product:
45
+ 'https://docs.commercetools.com/merchant-center/import-products#supported-headers-and-values',
46
+ 'inventory-entry':
47
+ 'https://docs.commercetools.com/merchant-center/import-inventory#supported-headers-and-values',
48
+ // TODO: remove `inventory` after aligning the resource type names in the Import API and Export API
49
+ inventory:
50
+ 'https://docs.commercetools.com/merchant-center/import-inventory#supported-headers-and-values',
51
+ 'discount-code':
52
+ 'https://docs.commercetools.com/merchant-center/import-discount-codes#supported-headers-and-values',
53
+ customer:
54
+ 'https://docs.commercetools.com/merchant-center/import-customers#supported-headers-and-values',
55
+ order:
56
+ 'https://docs.commercetools.com/merchant-center/import-orders#supported-headers-and-values',
57
+ 'product-type':
58
+ 'https://docs.commercetools.com/merchant-center/import-product-types#supported-headers-and-values',
59
+ 'business-unit':
60
+ 'https://docs.commercetools.com/merchant-center/import-business-units#supported-headers-and-values',
61
+ }
@@ -0,0 +1,17 @@
1
+ import type { BasicErrorDataType } from '../@types'
2
+
3
+ export class HttpError<T = BasicErrorDataType> extends Error {
4
+ readonly statusCode: number
5
+ readonly errorData?: T
6
+
7
+ constructor(statusCode: number, statusText?: string, errorData?: T) {
8
+ super(
9
+ `HTTP Error! Status code: ${statusCode}, message: "${
10
+ statusText ? statusText : ''
11
+ }"`
12
+ )
13
+ this.name = 'HttpError'
14
+ this.statusCode = statusCode
15
+ this.errorData = errorData
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ export * from './http-error'
2
+ export * from './invalid-response-error'
3
+ export * from './no-resources-to-export-error'
4
+ export * from './polling-aborted-error'
5
+ export * from './project-key-not-available-error'
6
+ export * from './query-predicate-error'
7
+ export * from './unexpected-column-error'
8
+ export * from './unexpected-operation-state-error'
9
+ export * from './unexpected-resource-type-error'
@@ -0,0 +1,6 @@
1
+ export class InvalidResponseError extends Error {
2
+ constructor(message: string) {
3
+ super(message)
4
+ this.name = 'InvalidResponseError'
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ export class NoResourcesToExportError extends Error {
2
+ constructor(message: string = 'There are no resources to export.') {
3
+ super(message)
4
+ this.name = 'NoResourcesToExportError'
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ export class PollingAbortedError extends Error {
2
+ constructor() {
3
+ super('Polling was aborted')
4
+ this.name = 'PollingAbortedError'
5
+ }
6
+ }
@@ -0,0 +1,6 @@
1
+ export class ProjectKeyNotAvailableError extends Error {
2
+ constructor(message: string = 'Project key is not available') {
3
+ super(message)
4
+ this.name = 'ProjectKeyNotAvailableError'
5
+ }
6
+ }
@@ -0,0 +1,10 @@
1
+ export class QueryPredicateError extends Error {
2
+ field = 'queryPredicate'
3
+
4
+ constructor(
5
+ message: string = 'There is an error with the query predicate. Make sure the syntax is correct.'
6
+ ) {
7
+ super(message)
8
+ this.name = 'QueryPredicateError'
9
+ }
10
+ }
@@ -0,0 +1,6 @@
1
+ export class UnexpectedColumnError extends Error {
2
+ constructor(columnName: string) {
3
+ super(`Unexpected column "${columnName}"`)
4
+ this.name = 'UnexpectedColumnError'
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ import { ProcessingState } from '@commercetools/importapi-sdk'
2
+
3
+ export class UnexpectedOperationStateError extends Error {
4
+ constructor(state: ProcessingState) {
5
+ super(`Unexpected operation state "${state}"`)
6
+ this.name = 'UnexpectedOperationStateError'
7
+ }
8
+ }
@@ -0,0 +1,6 @@
1
+ export class UnexpectedResourceTypeError extends Error {
2
+ constructor(resourceType: string) {
3
+ super(`Unexpected resource type "${resourceType}"`)
4
+ this.name = 'UnexpectedResourceTypeError'
5
+ }
6
+ }
@@ -0,0 +1,8 @@
1
+ export * from './use-fetch-export-operations'
2
+ export * from './use-fetch-file-import-job'
3
+ export * from './use-fetch-import-container-details'
4
+ export * from './use-fetch-import-operations'
5
+ export * from './use-fetch-import-summaries'
6
+ export * from './use-file-import-job-upload'
7
+ export * from './use-file-upload'
8
+ export * from './use-import-container-upload'
@@ -0,0 +1,34 @@
1
+ import React from 'react'
2
+ import { fetchExportOperations } from '../@api'
3
+ import { ProjectKeyNotAvailableError } from '../@errors'
4
+ import type {
5
+ PaginatedExportOperationResponse,
6
+ ExportOperationQueryParams,
7
+ } from '../@types'
8
+ import { useFetch } from './use-fetch'
9
+
10
+ type UseFetchExportOperationsConfig = {
11
+ projectKey: string
12
+ queryParams: ExportOperationQueryParams
13
+ pollingInterval?: number
14
+ shouldContinuePolling?: (data: PaginatedExportOperationResponse) => boolean
15
+ }
16
+
17
+ export const useFetchExportOperations = ({
18
+ projectKey,
19
+ queryParams,
20
+ pollingInterval,
21
+ shouldContinuePolling,
22
+ }: UseFetchExportOperationsConfig) => {
23
+ const fetchData = React.useCallback(() => {
24
+ if (!projectKey) {
25
+ return Promise.reject(new ProjectKeyNotAvailableError())
26
+ }
27
+ return fetchExportOperations({ projectKey, queryParams })
28
+ }, [projectKey, queryParams])
29
+
30
+ return useFetch(fetchData, {
31
+ pollingInterval,
32
+ shouldContinuePolling,
33
+ })
34
+ }
@@ -0,0 +1,131 @@
1
+ import { renderHook, waitFor } from '@testing-library/react'
2
+ import { getFileImportJob } from '../@api'
3
+ import { useFetchFileImportJob } from './use-fetch-file-import-job'
4
+ import type { FileImportJob } from '../@types'
5
+
6
+ jest.mock('@commercetools-frontend/sentry', () => ({
7
+ reportErrorToSentry: jest.fn(),
8
+ }))
9
+ jest.mock('../@api', () => ({
10
+ getFileImportJob: jest.fn(),
11
+ }))
12
+
13
+ const mockGetFileImportJob = getFileImportJob as jest.MockedFunction<
14
+ typeof getFileImportJob
15
+ >
16
+
17
+ describe('useFetchFileImportJob', () => {
18
+ const projectKey = 'test-with-big-data'
19
+ const resourceType = 'product'
20
+ const importContainerKey = 'test-container'
21
+ const jobId = 'test-job-id'
22
+
23
+ const mockJob: FileImportJob = {
24
+ id: jobId,
25
+ fileName: 'test.csv',
26
+ importContainerKey,
27
+ state: 'validated',
28
+ summary: {
29
+ total: 100,
30
+ invalid: 5,
31
+ valid: 95,
32
+ fieldsCount: 10,
33
+ fields: ['field1', 'field2'],
34
+ ignoredFields: [],
35
+ },
36
+ }
37
+
38
+ beforeEach(() => {
39
+ jest.clearAllMocks()
40
+ })
41
+
42
+ it('should fetch job details', async () => {
43
+ mockGetFileImportJob.mockResolvedValue(mockJob)
44
+
45
+ const { result } = renderHook(() =>
46
+ useFetchFileImportJob({
47
+ projectKey,
48
+ resourceType,
49
+ importContainerKey,
50
+ jobId,
51
+ })
52
+ )
53
+
54
+ await waitFor(() => {
55
+ expect(result.current.data).toEqual(mockJob)
56
+ })
57
+
58
+ expect(mockGetFileImportJob).toHaveBeenCalledWith({
59
+ projectKey,
60
+ resourceType,
61
+ importContainerKey,
62
+ jobId,
63
+ })
64
+ })
65
+
66
+ it('should handle errors', async () => {
67
+ const error = new Error('Failed to fetch job')
68
+ mockGetFileImportJob.mockRejectedValue(error)
69
+
70
+ const { result } = renderHook(() =>
71
+ useFetchFileImportJob({
72
+ projectKey,
73
+ resourceType,
74
+ importContainerKey,
75
+ jobId,
76
+ })
77
+ )
78
+
79
+ await waitFor(() => {
80
+ expect(result.current.error).toBeTruthy()
81
+ })
82
+ })
83
+
84
+ it('should pass polling config to useFetch', async () => {
85
+ mockGetFileImportJob.mockResolvedValue(mockJob)
86
+
87
+ const pollingInterval = 100
88
+ const shouldContinuePolling = jest.fn(
89
+ (data: typeof mockJob) => data.state !== 'validated'
90
+ )
91
+
92
+ const { result } = renderHook(() =>
93
+ useFetchFileImportJob({
94
+ projectKey,
95
+ resourceType,
96
+ importContainerKey,
97
+ jobId,
98
+ pollingInterval,
99
+ shouldContinuePolling,
100
+ })
101
+ )
102
+
103
+ await waitFor(() => {
104
+ expect(result.current.data).toEqual(mockJob)
105
+ })
106
+
107
+ expect(mockGetFileImportJob).toHaveBeenCalledWith({
108
+ projectKey,
109
+ resourceType,
110
+ importContainerKey,
111
+ jobId,
112
+ })
113
+ })
114
+
115
+ it('should throw ProjectKeyNotAvailableError when projectKey is empty', async () => {
116
+ const { result } = renderHook(() =>
117
+ useFetchFileImportJob({
118
+ projectKey: '',
119
+ resourceType,
120
+ importContainerKey,
121
+ jobId,
122
+ })
123
+ )
124
+
125
+ await waitFor(() => {
126
+ expect(result.current.error).toBeTruthy()
127
+ })
128
+
129
+ expect(result.current.error?.message).toBe('Project key is not available')
130
+ })
131
+ })
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { getFileImportJob } from '../@api'
3
+ import { ProjectKeyNotAvailableError } from '../@errors'
4
+ import type { FileImportJob } from '../@types'
5
+ import { useFetch } from './use-fetch'
6
+
7
+ type UseFetchFileImportJobConfig = {
8
+ projectKey: string
9
+ resourceType: string
10
+ importContainerKey: string
11
+ jobId: string
12
+ pollingInterval?: number
13
+ shouldContinuePolling?: (data: FileImportJob) => boolean
14
+ }
15
+
16
+ export const useFetchFileImportJob = ({
17
+ projectKey,
18
+ importContainerKey,
19
+ jobId,
20
+ pollingInterval,
21
+ shouldContinuePolling,
22
+ }: UseFetchFileImportJobConfig) => {
23
+ const fetchData = React.useCallback(() => {
24
+ if (!projectKey) {
25
+ return Promise.reject(new ProjectKeyNotAvailableError())
26
+ }
27
+ return getFileImportJob({
28
+ projectKey,
29
+ importContainerKey,
30
+ jobId,
31
+ })
32
+ }, [projectKey, importContainerKey, jobId])
33
+
34
+ return useFetch(fetchData, {
35
+ pollingInterval,
36
+ shouldContinuePolling,
37
+ })
38
+ }
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+ import { fetchImportContainerDetails } from '../@api'
3
+ import { ProjectKeyNotAvailableError } from '../@errors'
4
+ import { useFetch } from './use-fetch'
5
+ import { type ImportContainerDetails } from '../@types'
6
+
7
+ type UseFetchImportContainerDetailsConfig = {
8
+ projectKey: string
9
+ importContainerKey: string
10
+ pollingInterval?: number
11
+ shouldContinuePolling?: (data: ImportContainerDetails) => boolean
12
+ }
13
+
14
+ export const useFetchImportContainerDetails = ({
15
+ projectKey,
16
+ importContainerKey,
17
+ pollingInterval,
18
+ shouldContinuePolling,
19
+ }: UseFetchImportContainerDetailsConfig) => {
20
+ const fetchData = React.useCallback(() => {
21
+ if (!projectKey) {
22
+ return Promise.reject(new ProjectKeyNotAvailableError())
23
+ }
24
+ return fetchImportContainerDetails({ projectKey, importContainerKey })
25
+ }, [projectKey, importContainerKey])
26
+
27
+ return useFetch(fetchData, {
28
+ pollingInterval,
29
+ shouldContinuePolling,
30
+ })
31
+ }
@@ -0,0 +1,42 @@
1
+ import React from 'react'
2
+ import { fetchImportOperations } from '../@api'
3
+ import { ProjectKeyNotAvailableError } from '../@errors'
4
+ import type {
5
+ ExtendedImportOperationPagedResponse,
6
+ ImportOperationQueryParams,
7
+ } from '../@types'
8
+ import { useFetch } from './use-fetch'
9
+
10
+ type UseFetchImportOperationsConfig = {
11
+ projectKey: string
12
+ importContainerKey: string
13
+ queryParams: ImportOperationQueryParams
14
+ pollingInterval?: number
15
+ shouldContinuePolling?: (
16
+ data: ExtendedImportOperationPagedResponse
17
+ ) => boolean
18
+ }
19
+
20
+ export const useFetchImportOperations = ({
21
+ projectKey,
22
+ importContainerKey,
23
+ queryParams,
24
+ pollingInterval,
25
+ shouldContinuePolling,
26
+ }: UseFetchImportOperationsConfig) => {
27
+ const fetchData = React.useCallback(() => {
28
+ if (!projectKey) {
29
+ return Promise.reject(new ProjectKeyNotAvailableError())
30
+ }
31
+ return fetchImportOperations({
32
+ projectKey,
33
+ importContainerKey,
34
+ queryParams,
35
+ })
36
+ }, [projectKey, importContainerKey, queryParams])
37
+
38
+ return useFetch(fetchData, {
39
+ pollingInterval,
40
+ shouldContinuePolling,
41
+ })
42
+ }
@@ -0,0 +1,47 @@
1
+ import React from 'react'
2
+ import { fetchImportSummaries } from '../@api'
3
+ import { ProjectKeyNotAvailableError } from '../@errors'
4
+ import type {
5
+ ImportContainerQueryParams,
6
+ ImportContainerDetails,
7
+ } from '../@types'
8
+ import { useFetch } from './use-fetch'
9
+
10
+ export type ImportSummariesResult = {
11
+ results: ImportContainerDetails[]
12
+ count: number
13
+ total: number
14
+ }
15
+
16
+ type UseFetchImportSummariesConfig = {
17
+ projectKey: string
18
+ queryParams: ImportContainerQueryParams
19
+ pollingInterval?: number
20
+ shouldContinuePolling?: (data: ImportSummariesResult) => boolean
21
+ }
22
+
23
+ export const useFetchImportSummaries = ({
24
+ projectKey,
25
+ queryParams,
26
+ pollingInterval,
27
+ shouldContinuePolling,
28
+ }: UseFetchImportSummariesConfig) => {
29
+ const fetchData =
30
+ React.useCallback(async (): Promise<ImportSummariesResult> => {
31
+ if (!projectKey) {
32
+ return Promise.reject(new ProjectKeyNotAvailableError())
33
+ }
34
+ const summary = await fetchImportSummaries({ projectKey, queryParams })
35
+ const resolvedResults = await Promise.all(summary.results)
36
+ return {
37
+ results: resolvedResults,
38
+ count: summary.count,
39
+ total: summary.total,
40
+ }
41
+ }, [projectKey, queryParams])
42
+
43
+ return useFetch<ImportSummariesResult>(fetchData, {
44
+ pollingInterval,
45
+ shouldContinuePolling,
46
+ })
47
+ }