@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,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
|
+
}
|