@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,19 @@
1
+ export declare function getFileUploadErrorsCount(errors?: Array<{
2
+ errors: Array<unknown>;
3
+ }>): number;
4
+ export declare function mapUploadFileErrorsResponseToUploadFileErrorRows(uploadFileErrorsResponse?: Array<{
5
+ row?: number;
6
+ index?: number;
7
+ errors: Array<{
8
+ field: string;
9
+ code: string;
10
+ message: string;
11
+ }>;
12
+ }>): Array<{
13
+ id: string;
14
+ row?: number;
15
+ index?: number;
16
+ field: string;
17
+ code: string;
18
+ validationMessage: string;
19
+ }>;
@@ -0,0 +1,12 @@
1
+ import type { ResourceTypeId } from '@commercetools/importapi-sdk';
2
+ import type { FileImportJob } from "../@types/index.js";
3
+ export declare function getFileImportJobFileType(resourceType: ResourceTypeId): 'csv' | 'json';
4
+ export declare function toImportApiResourceType(resourceType: string): string;
5
+ export declare function isImportJobQueued(job?: FileImportJob): boolean;
6
+ export declare function isImportJobProcessing(job?: FileImportJob): boolean;
7
+ export declare function isImportJobValidated(job?: FileImportJob): boolean;
8
+ export declare function isImportJobInitializing(job?: FileImportJob): boolean;
9
+ export declare function isImportJobReady(job?: FileImportJob): boolean;
10
+ export declare function isImportJobRejected(job?: FileImportJob): boolean;
11
+ export declare function isImportJobTerminal(job?: FileImportJob): boolean;
12
+ export declare function shouldContinuePollingForImportValidation(job?: FileImportJob): boolean;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Convert megabytes to bytes
3
+ */
4
+ export declare const toBytes: (megabytes: number) => number;
5
+ /**
6
+ * Returns the number of rows in a CSV file excluding the header
7
+ * @param file The CSV file to process
8
+ * @returns A promise that resolves to the number of rows
9
+ */
10
+ export declare const getRowCount: (file: File) => Promise<number>;
11
+ /**
12
+ * Check if a CSV file has a single key column
13
+ * @param file The CSV file to check
14
+ * @returns A promise that resolves to true if the file has only one column named 'key'
15
+ */
16
+ export declare const hasSingleKeyColumn: (file: File) => Promise<boolean>;
17
+ /**
18
+ * Validate delimiter in a CSV file
19
+ * @param file The CSV file to validate
20
+ * @param allowedDelimiters Array of allowed delimiters
21
+ * @returns A promise that resolves to false if delimiter is invalid
22
+ */
23
+ export declare const validateDelimiter: (file: File, allowedDelimiters: string[]) => Promise<boolean>;
24
+ /**
25
+ * Returns columns from the provided `columns` excluding those specified in the `ignoredColumns`
26
+ */
27
+ export declare const getValidatedColumns: (columns: string[], ignoredColumns: string[]) => string[];
28
+ /**
29
+ * Count items in a JSON file
30
+ * @param file The JSON file to process
31
+ * @returns Object with isValid flag and optional itemsCount
32
+ */
33
+ export declare const countJsonFileItems: (file: File) => Promise<{
34
+ isValid: false;
35
+ } | {
36
+ isValid: true;
37
+ itemsCount: number;
38
+ }>;
39
+ /**
40
+ * Count unique resources in a CSV file by counting unique values in the "key" column.
41
+ * A single resource can span multiple rows (when it has array fields like variants, assets...),
42
+ * so we count unique keys rather than rows.
43
+ * @param file The CSV file to process
44
+ * @returns A promise that resolves to the number of unique resources
45
+ */
46
+ export declare const countUniqueResourcesInCsv: (file: File) => Promise<number>;
47
+ /**
48
+ * Map file upload errors to upload file error rows with unique IDs
49
+ * @param uploadFileErrors Array of file upload errors
50
+ * @returns Array of upload file errors with unique id field
51
+ */
52
+ export declare const mapFileUploadErrorsToUploadFileErrorRows: <T extends Record<string, unknown>>(uploadFileErrors: T[]) => (T & {
53
+ id: string;
54
+ })[];
@@ -0,0 +1 @@
1
+ export declare const mapFormikErrors: (error: string | string[] | undefined | Record<string, boolean>) => Record<string, boolean>;
@@ -0,0 +1,5 @@
1
+ export declare function formatErrorCode(code: string): string;
2
+ export declare function extractErrorDescriptionFromValidationMessage(message: string): string | undefined;
3
+ export declare function formatKeys(obj: Record<string, unknown>, conjunction?: string): string;
4
+ export declare function appendCsvOrJsonExtensionIfAbsent(fileName: string, fileFormat: 'csv' | 'json'): string;
5
+ export declare function convertFileSizeToKB(sizeInBytes: number): number;
@@ -0,0 +1,8 @@
1
+ export declare const encodeFileNameWithTimestampToContainerKey: (fileName: string) => string;
2
+ /**
3
+ * Check if an import container is a file upload import
4
+ * @param tags - Array of tags from the import container
5
+ * @returns True if the tags indicate this is a file upload import
6
+ */
7
+ export declare const checkIfFileUploadImport: (tags?: string[]) => boolean;
8
+ export declare const decodeFileNameFromImportContainerKey: (importContainerKey: string) => string;
@@ -0,0 +1,8 @@
1
+ export * from "./error-mapping.js";
2
+ export * from "./file-import-job-helpers.js";
3
+ export * from "./file-upload.js";
4
+ export * from "./form.js";
5
+ export * from "./format.js";
6
+ export * from "./import-container.js";
7
+ export * from "./poll-job-until-validated.js";
8
+ export * from "./url.js";
@@ -0,0 +1,11 @@
1
+ import type { FileImportJob } from "../@types/index.js";
2
+ export type PollJobUntilValidatedConfig = {
3
+ projectKey: string;
4
+ jobId: string;
5
+ importContainerKey: string;
6
+ pollingInterval?: number;
7
+ maxAttempts?: number;
8
+ onJobUpdate?: (job: FileImportJob) => void;
9
+ abortSignal?: AbortSignal;
10
+ };
11
+ export declare const pollJobUntilValidated: ({ projectKey, jobId, importContainerKey, pollingInterval, maxAttempts, onJobUpdate, abortSignal, }: PollJobUntilValidatedConfig) => Promise<FileImportJob>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Formats an object into a URL query string
3
+ * @param queryParams - Object containing query parameters
4
+ * @returns Formatted query string with leading '?' or empty string
5
+ */
6
+ export declare function formatQueryString(queryParams?: Record<string, unknown>): string;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared package for operations
3
+ *
4
+ * This package provides shared functionality for import/export operations across:
5
+ * - application-operations
6
+ * - import-resources-modal
7
+ * - export-resources-modal
8
+ * - delete-resources-modal
9
+ * - unpublish-products-modal
10
+ *
11
+ * Public API includes:
12
+ * - Hooks: useImportContainerUpload, useFetch*, etc.
13
+ * - Components: FileDropArea, UploadingModal, UploadSettings, etc.
14
+ * - Utils: File validation, error mapping, formatting, etc.
15
+ * - Constants: Delimiters, upload limits, resource links, etc.
16
+ * - Types: All TypeScript types and interfaces
17
+ * - Errors: Custom error classes
18
+ * - API: Functions for interacting with import/export APIs
19
+ */
20
+ export * from "./@api/index.js";
21
+ export * from "./@components/index.js";
22
+ export * from "./@constants/index.js";
23
+ export * from "./@errors/index.js";
24
+ export * from "./@hooks/index.js";
25
+ export * from "./@types/index.js";
26
+ export * from "./@utils/index.js";
package/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './src'
@@ -0,0 +1,11 @@
1
+ process.env.ENABLE_NEW_JSX_TRANSFORM = 'true'
2
+
3
+ const basePreset = require('@commercetools-frontend/jest-preset-mc-app/typescript')
4
+
5
+ /** @type {import('jest').Config} */
6
+ const jestPreset = {
7
+ ...basePreset,
8
+ displayName: '@commercetools-frontend-extensions/operations',
9
+ }
10
+
11
+ module.exports = jestPreset
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@commercetools-frontend-extensions/operations",
3
+ "version": "0.0.0-canary-20251209161906",
4
+ "license": "Proprietary",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "main": "dist/commercetools-frontend-extensions-operations.cjs.js",
9
+ "module": "dist/commercetools-frontend-extensions-operations.esm.js",
10
+ "types": "dist/commercetools-frontend-extensions-operations.cjs.d.ts",
11
+ "dependencies": {
12
+ "@babel/core": "^7.21.0",
13
+ "@babel/runtime": "7.28.4",
14
+ "@babel/runtime-corejs3": "7.28.4",
15
+ "@commercetools/importapi-sdk": "5.20.0",
16
+ "papaparse": "5.5.3",
17
+ "pluralize": "8.0.0",
18
+ "react-dropzone": "14.3.8"
19
+ },
20
+ "devDependencies": {
21
+ "@commercetools-frontend/actions-global": "24.11.0",
22
+ "@commercetools-frontend/application-components": "24.11.0",
23
+ "@commercetools-frontend/application-shell": "24.11.0",
24
+ "@commercetools-frontend/application-shell-connectors": "24.11.0",
25
+ "@commercetools-frontend/constants": "24.11.0",
26
+ "@commercetools-frontend/jest-preset-mc-app": "24.11.0",
27
+ "@commercetools-frontend/permissions": "24.11.0",
28
+ "@commercetools-frontend/sentry": "24.11.0",
29
+ "@commercetools-frontend/ui-kit": "20.3.0",
30
+ "@emotion/react": "11.14.0",
31
+ "@emotion/styled": "11.14.1",
32
+ "@testing-library/react": "16.1.0",
33
+ "@types/jest": "29.5.14",
34
+ "@types/papaparse": "5.5.1",
35
+ "@types/pluralize": "0.0.33",
36
+ "@types/react": "19.2.0",
37
+ "msw": "1.3.5",
38
+ "react": "19.2.0",
39
+ "react-intl": "7.1.4",
40
+ "rimraf": "6.1.2",
41
+ "typescript": "5.2.2"
42
+ },
43
+ "peerDependencies": {
44
+ "@commercetools-frontend/actions-global": "23.x || 24.x",
45
+ "@commercetools-frontend/application-components": "23.x || 24.x",
46
+ "@commercetools-frontend/application-shell": "23.x || 24.x",
47
+ "@commercetools-frontend/application-shell-connectors": "23.x || 24.x",
48
+ "@commercetools-frontend/constants": "23.x || 24.x",
49
+ "@commercetools-frontend/permissions": "23.x || 24.x",
50
+ "@commercetools-frontend/sentry": "23.x || 24.x",
51
+ "@commercetools-frontend/ui-kit": "19.x || 20.x",
52
+ "@emotion/react": "11.x",
53
+ "@emotion/styled": "11.x",
54
+ "react": "17.x || 19.x"
55
+ },
56
+ "scripts": {
57
+ "build": "rimraf dist && preconstruct build",
58
+ "test": "jest --config jest.test.config.js",
59
+ "test:watch": "jest --config jest.test.config.js --watch",
60
+ "typecheck": "tsc --noEmit"
61
+ }
62
+ }
@@ -0,0 +1,26 @@
1
+ import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants'
2
+ import {
3
+ assertPaginatedExportOperationResponse,
4
+ type PaginatedExportOperationResponse,
5
+ type ExportOperationQueryParams,
6
+ } from '../@types'
7
+ import { fetcher } from './fetcher'
8
+ import { getExportOperationsURL } from './urls'
9
+
10
+ export async function fetchExportOperations({
11
+ projectKey,
12
+ queryParams,
13
+ }: {
14
+ projectKey: string
15
+ queryParams: ExportOperationQueryParams
16
+ }): Promise<PaginatedExportOperationResponse> {
17
+ const exportOperations = await fetcher<PaginatedExportOperationResponse>({
18
+ url: getExportOperationsURL({ projectKey, queryParams }),
19
+ config: {
20
+ proxy: MC_API_PROXY_TARGETS.EXPORT,
21
+ method: 'GET',
22
+ },
23
+ })
24
+ assertPaginatedExportOperationResponse(exportOperations)
25
+ return exportOperations
26
+ }
@@ -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,137 @@
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
+ if (config.abortSignal) {
126
+ if (config.abortSignal.aborted) {
127
+ xhr.abort()
128
+ } else {
129
+ config.abortSignal.addEventListener('abort', () => xhr.abort(), {
130
+ once: true,
131
+ })
132
+ }
133
+ }
134
+
135
+ xhr.send(payload)
136
+ return xhr
137
+ }
@@ -0,0 +1,217 @@
1
+ import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants'
2
+ import type {
3
+ FileImportJob,
4
+ CreateFileImportJobParameters,
5
+ GetFileImportJobParameters,
6
+ GetFileImportJobRecordsParameters,
7
+ ProcessFileImportJobParameters,
8
+ ProcessFileImportJobResponse,
9
+ DeleteFileImportJobParameters,
10
+ ListFileImportJobsParameters,
11
+ ListFileImportJobsResponse,
12
+ FileImportJobRecordsResponse,
13
+ } from '../@types'
14
+ import {
15
+ assertFileImportJob,
16
+ assertFileImportJobRecordsResponse,
17
+ assertProcessFileImportJobResponse,
18
+ assertListFileImportJobsResponse,
19
+ } from '../@types'
20
+ import { fetcher, fetchUsingXhr } from './fetcher'
21
+ import {
22
+ getFileImportJobsURL,
23
+ getFileImportJobByIdURL,
24
+ getFileImportJobRecordsURL,
25
+ getFileImportJobProcessURL,
26
+ getFileImportJobDeleteURL,
27
+ getFileImportJobsListURL,
28
+ } from './urls'
29
+ import { formatQueryString } from '../@utils'
30
+
31
+ export function createFileImportJob({
32
+ projectKey,
33
+ resourceType,
34
+ importContainerKey,
35
+ payload,
36
+ onProgress,
37
+ abortSignal,
38
+ }: CreateFileImportJobParameters): Promise<FileImportJob> {
39
+ const url = getFileImportJobsURL({
40
+ projectKey,
41
+ resourceType,
42
+ importContainerKey,
43
+ })
44
+
45
+ if ('fileType' in payload) {
46
+ return new Promise((resolve, reject) => {
47
+ const formData = new FormData()
48
+ formData.append('fileType', payload.fileType)
49
+ formData.append('fileName', payload.fileName)
50
+ formData.append('file', payload.file, payload.fileName)
51
+
52
+ fetchUsingXhr({
53
+ url,
54
+ payload: formData,
55
+ config: {
56
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
57
+ method: 'POST',
58
+ headers: {
59
+ 'Content-Type': null,
60
+ },
61
+ abortSignal,
62
+ },
63
+ onProgress: onProgress || (() => {}),
64
+ onSuccess: (response: FileImportJob) => {
65
+ assertFileImportJob(response)
66
+ resolve(response)
67
+ },
68
+ onError: reject,
69
+ })
70
+ })
71
+ }
72
+
73
+ return fetcher<FileImportJob>({
74
+ url,
75
+ payload: JSON.stringify(payload),
76
+ config: {
77
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
78
+ method: 'POST',
79
+ headers: {
80
+ accept: 'application/json',
81
+ 'Content-Type': 'application/json',
82
+ },
83
+ },
84
+ }).then((response) => {
85
+ assertFileImportJob(response)
86
+ return response
87
+ })
88
+ }
89
+
90
+ export async function getFileImportJob({
91
+ projectKey,
92
+ importContainerKey,
93
+ jobId,
94
+ }: GetFileImportJobParameters): Promise<FileImportJob> {
95
+ const url = getFileImportJobByIdURL({
96
+ projectKey,
97
+ importContainerKey,
98
+ jobId,
99
+ })
100
+
101
+ const response = await fetcher<FileImportJob>({
102
+ url,
103
+ config: {
104
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
105
+ method: 'GET',
106
+ },
107
+ })
108
+
109
+ assertFileImportJob(response)
110
+ return response
111
+ }
112
+
113
+ export async function getFileImportJobRecords({
114
+ projectKey,
115
+ importContainerKey,
116
+ jobId,
117
+ limit,
118
+ offset,
119
+ isValid,
120
+ }: GetFileImportJobRecordsParameters): Promise<FileImportJobRecordsResponse> {
121
+ const baseUrl = getFileImportJobRecordsURL({
122
+ projectKey,
123
+ importContainerKey,
124
+ jobId,
125
+ })
126
+ const queryString = formatQueryString({ limit, offset, isValid })
127
+ const url = `${baseUrl}${queryString}`
128
+
129
+ const response = await fetcher<FileImportJobRecordsResponse>({
130
+ url,
131
+ config: {
132
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
133
+ method: 'GET',
134
+ },
135
+ })
136
+
137
+ assertFileImportJobRecordsResponse(response)
138
+ return response
139
+ }
140
+
141
+ export async function processFileImportJob({
142
+ projectKey,
143
+ resourceType,
144
+ importContainerKey,
145
+ jobId,
146
+ action,
147
+ }: ProcessFileImportJobParameters): Promise<ProcessFileImportJobResponse> {
148
+ const url = getFileImportJobProcessURL({
149
+ projectKey,
150
+ resourceType,
151
+ importContainerKey,
152
+ jobId,
153
+ })
154
+
155
+ const payload = action ? { action } : {}
156
+
157
+ const response = await fetcher<ProcessFileImportJobResponse>({
158
+ url,
159
+ payload: JSON.stringify(payload),
160
+ config: {
161
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
162
+ method: 'POST',
163
+ headers: {
164
+ accept: 'application/json',
165
+ 'Content-Type': 'application/json',
166
+ },
167
+ },
168
+ })
169
+
170
+ assertProcessFileImportJobResponse(response)
171
+ return response
172
+ }
173
+
174
+ export async function deleteFileImportJob({
175
+ projectKey,
176
+ importContainerKey,
177
+ jobId,
178
+ }: DeleteFileImportJobParameters): Promise<void> {
179
+ const url = getFileImportJobDeleteURL({
180
+ projectKey,
181
+ importContainerKey,
182
+ jobId,
183
+ })
184
+
185
+ await fetcher<void>({
186
+ url,
187
+ config: {
188
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
189
+ method: 'DELETE',
190
+ },
191
+ })
192
+ }
193
+
194
+ export async function listFileImportJobs({
195
+ projectKey,
196
+ importContainerKey,
197
+ limit,
198
+ offset,
199
+ }: ListFileImportJobsParameters): Promise<ListFileImportJobsResponse> {
200
+ const baseUrl = getFileImportJobsListURL({
201
+ projectKey,
202
+ importContainerKey,
203
+ })
204
+ const queryString = formatQueryString({ limit, offset })
205
+ const url = `${baseUrl}${queryString}`
206
+
207
+ const response = await fetcher<ListFileImportJobsResponse>({
208
+ url,
209
+ config: {
210
+ proxy: MC_API_PROXY_TARGETS.IMPORT,
211
+ method: 'GET',
212
+ },
213
+ })
214
+
215
+ assertListFileImportJobsResponse(response)
216
+ return response
217
+ }