@commercetools-frontend-extensions/operations 3.1.2 → 3.2.1
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 +12 -0
- package/README.md +1 -2
- package/dist/commercetools-frontend-extensions-operations.cjs.dev.js +84 -72
- package/dist/commercetools-frontend-extensions-operations.cjs.prod.js +84 -72
- package/dist/commercetools-frontend-extensions-operations.esm.js +83 -68
- package/dist/declarations/src/@api/file-import-jobs.d.ts +21 -0
- package/dist/declarations/src/@api/import-containers.d.ts +5 -1
- package/dist/declarations/src/@hooks/use-file-upload.d.ts +0 -1
- package/dist/declarations/src/@types/file-import-job.d.ts +7 -1
- package/dist/declarations/src/@utils/file-upload.d.ts +0 -8
- package/package.json +9 -9
- package/src/@api/file-import-jobs.ts +52 -0
- package/src/@api/import-containers.ts +34 -3
- package/src/@components/uploading-modal/uploading-modal.tsx +6 -4
- package/src/@hooks/use-file-upload.ts +2 -15
- package/src/@types/file-import-job.ts +12 -11
- package/src/@utils/file-upload.ts +0 -39
|
@@ -18,8 +18,8 @@ import _inherits from '@babel/runtime-corejs3/helpers/esm/inherits';
|
|
|
18
18
|
import _wrapNativeSuper from '@babel/runtime-corejs3/helpers/esm/wrapNativeSuper';
|
|
19
19
|
import { MC_API_PROXY_TARGETS } from '@commercetools-frontend/constants';
|
|
20
20
|
import _everyInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/every';
|
|
21
|
-
import _Array$isArray from '@babel/runtime-corejs3/core-js-stable/array/is-array';
|
|
22
21
|
import { plural } from 'pluralize';
|
|
22
|
+
import _Array$isArray from '@babel/runtime-corejs3/core-js-stable/array/is-array';
|
|
23
23
|
import _reduceInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/reduce';
|
|
24
24
|
import _flatMapInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/flat-map';
|
|
25
25
|
import _mapInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/map';
|
|
@@ -27,13 +27,10 @@ import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';
|
|
|
27
27
|
import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find';
|
|
28
28
|
import _includesInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/includes';
|
|
29
29
|
import _someInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/some';
|
|
30
|
-
import _Set from '@babel/runtime-corejs3/core-js-stable/set';
|
|
31
|
-
import _findIndexInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find-index';
|
|
32
|
-
import _trimInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/trim';
|
|
33
|
-
import _bindInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/bind';
|
|
34
30
|
import Papa from 'papaparse';
|
|
35
31
|
import _sliceInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/slice';
|
|
36
32
|
import _JSON$stringify from '@babel/runtime-corejs3/core-js-stable/json/stringify';
|
|
33
|
+
import _bindInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/bind';
|
|
37
34
|
import _startsWithInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/starts-with';
|
|
38
35
|
import _setTimeout from '@babel/runtime-corejs3/core-js-stable/set-timeout';
|
|
39
36
|
import _concatInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/concat';
|
|
@@ -331,14 +328,9 @@ function assertProcessFileImportJobResponse(maybeResponse) {
|
|
|
331
328
|
throw new Error('Invalid Process File Import Job response');
|
|
332
329
|
}
|
|
333
330
|
function assertListFileImportJobsResponse(maybeResponse) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (maybeResponse.length > 0) {
|
|
338
|
-
const requiredFields = ['id', 'fileName', 'importContainerKey', 'state'];
|
|
339
|
-
if (!hasRequiredFields(maybeResponse[0], requiredFields)) {
|
|
340
|
-
throw new Error('Invalid List File Import Jobs response: missing required fields');
|
|
341
|
-
}
|
|
331
|
+
const requiredFields = ['results', 'total', 'limit', 'offset', 'count'];
|
|
332
|
+
if (!hasRequiredFields(maybeResponse, requiredFields)) {
|
|
333
|
+
throw new Error('Invalid List File Import Jobs response: missing required fields');
|
|
342
334
|
}
|
|
343
335
|
}
|
|
344
336
|
|
|
@@ -451,7 +443,7 @@ function shouldContinuePollingForImportValidation(job) {
|
|
|
451
443
|
}
|
|
452
444
|
|
|
453
445
|
function ownKeys$8(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
454
|
-
function _objectSpread$8(e) { for (var r = 1; r < arguments.length; r++) { var
|
|
446
|
+
function _objectSpread$8(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context2 = ownKeys$8(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context3 = ownKeys$8(Object(t))).call(_context3, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
455
447
|
|
|
456
448
|
/**
|
|
457
449
|
* Convert megabytes to bytes
|
|
@@ -570,44 +562,6 @@ const countJsonFileItems = async file => {
|
|
|
570
562
|
}
|
|
571
563
|
};
|
|
572
564
|
|
|
573
|
-
/**
|
|
574
|
-
* Count unique resources in a CSV file by counting unique values in the "key" column.
|
|
575
|
-
* A single resource can span multiple rows (when it has array fields like variants, assets...),
|
|
576
|
-
* so we count unique keys rather than rows.
|
|
577
|
-
* @param file The CSV file to process
|
|
578
|
-
* @returns A promise that resolves to the number of unique resources
|
|
579
|
-
*/
|
|
580
|
-
const countUniqueResourcesInCsv = file => {
|
|
581
|
-
return new _Promise(resolve => {
|
|
582
|
-
const uniqueKeys = new _Set();
|
|
583
|
-
let keyColumnIndex = -1;
|
|
584
|
-
let isFirstRow = true;
|
|
585
|
-
Papa.parse(file, {
|
|
586
|
-
step: _ref2 => {
|
|
587
|
-
var _context4, _context5;
|
|
588
|
-
let data = _ref2.data;
|
|
589
|
-
if (!_Array$isArray(data)) return;
|
|
590
|
-
if (isFirstRow) {
|
|
591
|
-
keyColumnIndex = _findIndexInstanceProperty(data).call(data, col => {
|
|
592
|
-
var _context2, _context3;
|
|
593
|
-
return ((_context2 = col?.toLowerCase()) == null ? void 0 : _bindInstanceProperty(_context3 = Function.call).call(_context3, _trimInstanceProperty(_context2), _context2))?.() === 'key';
|
|
594
|
-
});
|
|
595
|
-
isFirstRow = false;
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
if (keyColumnIndex === -1) return;
|
|
599
|
-
const keyValue = ((_context4 = data[keyColumnIndex]) == null ? void 0 : _bindInstanceProperty(_context5 = Function.call).call(_context5, _trimInstanceProperty(_context4), _context4))?.();
|
|
600
|
-
if (keyValue) {
|
|
601
|
-
uniqueKeys.add(keyValue);
|
|
602
|
-
}
|
|
603
|
-
},
|
|
604
|
-
complete: () => {
|
|
605
|
-
resolve(uniqueKeys.size);
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
});
|
|
609
|
-
};
|
|
610
|
-
|
|
611
565
|
/**
|
|
612
566
|
* Map file upload errors to upload file error rows with unique IDs
|
|
613
567
|
* @param uploadFileErrors Array of file upload errors
|
|
@@ -1120,10 +1074,48 @@ async function listFileImportJobs(_ref6) {
|
|
|
1120
1074
|
assertListFileImportJobsResponse(response);
|
|
1121
1075
|
return response;
|
|
1122
1076
|
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Gets the file import job info for an import container
|
|
1079
|
+
*
|
|
1080
|
+
* For the new file import job flow, import operations are created incrementally
|
|
1081
|
+
* during the 'initialising' state. The import summary total
|
|
1082
|
+
* reflects only the operations created so far, which can be misleading
|
|
1083
|
+
*
|
|
1084
|
+
* This helper fetches the file import job (if it exists) to get:
|
|
1085
|
+
* - The true total from the job summary (known from initial CSV validation)
|
|
1086
|
+
* - Whether the job is still initializing (creating import operations)
|
|
1087
|
+
*
|
|
1088
|
+
* @returns Job info if found, null otherwise
|
|
1089
|
+
*/
|
|
1090
|
+
async function getFileImportJobInfoForContainer(_ref7) {
|
|
1091
|
+
let projectKey = _ref7.projectKey,
|
|
1092
|
+
importContainerKey = _ref7.importContainerKey;
|
|
1093
|
+
try {
|
|
1094
|
+
const response = await listFileImportJobs({
|
|
1095
|
+
projectKey,
|
|
1096
|
+
importContainerKey,
|
|
1097
|
+
limit: 1
|
|
1098
|
+
});
|
|
1099
|
+
if (response.results.length > 0 && response.results[0].summary?.total != null) {
|
|
1100
|
+
var _context;
|
|
1101
|
+
const job = response.results[0];
|
|
1102
|
+
const isInitializing = _includesInstanceProperty(_context = ['processing', 'initialising']).call(_context, job.state);
|
|
1103
|
+
return {
|
|
1104
|
+
total: job.summary.total,
|
|
1105
|
+
isInitializing
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
return null;
|
|
1109
|
+
} catch {
|
|
1110
|
+
// Job might not exist (old flow)
|
|
1111
|
+
return null;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1123
1114
|
|
|
1124
1115
|
function ownKeys$6(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
1125
1116
|
function _objectSpread$6(e) { for (var r = 1; r < arguments.length; r++) { var _context2, _context3; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context2 = ownKeys$6(Object(t), !0)).call(_context2, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context3 = ownKeys$6(Object(t))).call(_context3, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
1126
1117
|
function getImportState(importSummary) {
|
|
1118
|
+
let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
1127
1119
|
const processing = importSummary.states.processing > 0;
|
|
1128
1120
|
if (processing) return ImportStates.Processing;
|
|
1129
1121
|
const waitForUnresolvedReferences = importSummary.states.waitForMasterVariant > 0 || importSummary.states.unresolved > 0;
|
|
@@ -1132,6 +1124,11 @@ function getImportState(importSummary) {
|
|
|
1132
1124
|
if (partiallyCompleted) return ImportStates.PartiallyCompleted;
|
|
1133
1125
|
const noRunning = importSummary.total === 0;
|
|
1134
1126
|
if (noRunning) return ImportStates.NoRunningImports;
|
|
1127
|
+
|
|
1128
|
+
// For the new flow: job is actively creating import operations (to show as Processing even if no operations exist yet)
|
|
1129
|
+
if (options.isJobInitializing) {
|
|
1130
|
+
return ImportStates.Processing;
|
|
1131
|
+
}
|
|
1135
1132
|
const successfullyCompleted = importSummary.states.imported === importSummary.total || importSummary.states.deleted === importSummary.total;
|
|
1136
1133
|
if (successfullyCompleted) return ImportStates.SuccessfullyCompleted;
|
|
1137
1134
|
const failed = importSummary.states.rejected + importSummary.states.validationFailed === importSummary.total;
|
|
@@ -1280,12 +1277,33 @@ async function cancelImportContainerByKey(_ref8) {
|
|
|
1280
1277
|
return response;
|
|
1281
1278
|
}
|
|
1282
1279
|
async function importContainerToContainerDetails(projectKey, importContainer) {
|
|
1283
|
-
|
|
1280
|
+
let importSummary = await fetchImportSummary({
|
|
1284
1281
|
projectKey,
|
|
1285
1282
|
importContainerKey: importContainer.key
|
|
1286
1283
|
});
|
|
1287
|
-
const importState = getImportState(importSummary);
|
|
1288
1284
|
const isFileUploadImport = checkIfFileUploadImport(importContainer.tags);
|
|
1285
|
+
|
|
1286
|
+
// For the new file import job flow the import operations are created incrementally
|
|
1287
|
+
// The import summary total reflects only operations created so far
|
|
1288
|
+
// Only override total when job is actively initializing (creating operations)
|
|
1289
|
+
let isJobInitializing = false;
|
|
1290
|
+
if (isFileUploadImport) {
|
|
1291
|
+
const jobInfo = await getFileImportJobInfoForContainer({
|
|
1292
|
+
projectKey,
|
|
1293
|
+
importContainerKey: importContainer.key
|
|
1294
|
+
});
|
|
1295
|
+
if (jobInfo !== null) {
|
|
1296
|
+
isJobInitializing = jobInfo.isInitializing;
|
|
1297
|
+
if (isJobInitializing || importSummary.total > 0) {
|
|
1298
|
+
importSummary = _objectSpread$6(_objectSpread$6({}, importSummary), {}, {
|
|
1299
|
+
total: jobInfo.total
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
const importState = getImportState(importSummary, {
|
|
1305
|
+
isJobInitializing
|
|
1306
|
+
});
|
|
1289
1307
|
return {
|
|
1290
1308
|
importContainer: importContainer,
|
|
1291
1309
|
importState,
|
|
@@ -2563,13 +2581,16 @@ const UploadingModal = _ref => {
|
|
|
2563
2581
|
label: cancelLabel,
|
|
2564
2582
|
onClick: onCancel
|
|
2565
2583
|
})]
|
|
2566
|
-
}), /*#__PURE__*/
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2584
|
+
}), /*#__PURE__*/jsxs(Spacings.Stack, {
|
|
2585
|
+
scale: "xs",
|
|
2586
|
+
children: [/*#__PURE__*/jsx(ProgressBar, {
|
|
2587
|
+
barWidth: "scale",
|
|
2588
|
+
height: "10",
|
|
2589
|
+
progress: progress
|
|
2590
|
+
}), statusMessage && /*#__PURE__*/jsx(Text.Detail, {
|
|
2591
|
+
tone: "tertiary",
|
|
2592
|
+
children: statusMessage
|
|
2593
|
+
})]
|
|
2573
2594
|
})]
|
|
2574
2595
|
})
|
|
2575
2596
|
});
|
|
@@ -2992,7 +3013,6 @@ const useFileUpload = _ref2 => {
|
|
|
2992
3013
|
setProgress = _React$useState4[1];
|
|
2993
3014
|
const _React$useState5 = React.useState({
|
|
2994
3015
|
processed: 0,
|
|
2995
|
-
total: 0,
|
|
2996
3016
|
isValidating: false
|
|
2997
3017
|
}),
|
|
2998
3018
|
_React$useState6 = _slicedToArray(_React$useState5, 2),
|
|
@@ -3009,7 +3029,6 @@ const useFileUpload = _ref2 => {
|
|
|
3009
3029
|
setProgress(0);
|
|
3010
3030
|
setValidationProgress({
|
|
3011
3031
|
processed: 0,
|
|
3012
|
-
total: 0,
|
|
3013
3032
|
isValidating: false
|
|
3014
3033
|
});
|
|
3015
3034
|
}, []);
|
|
@@ -3018,7 +3037,6 @@ const useFileUpload = _ref2 => {
|
|
|
3018
3037
|
setProgress(0);
|
|
3019
3038
|
try {
|
|
3020
3039
|
if (useJobBasedFlow) {
|
|
3021
|
-
const totalResources = config.skipValidationPolling ? 0 : await countUniqueResourcesInCsv(config.file);
|
|
3022
3040
|
await jobUpload.upload({
|
|
3023
3041
|
file: config.file,
|
|
3024
3042
|
resourceType: config.resourceType,
|
|
@@ -3065,7 +3083,6 @@ const useFileUpload = _ref2 => {
|
|
|
3065
3083
|
try {
|
|
3066
3084
|
setValidationProgress({
|
|
3067
3085
|
processed: 0,
|
|
3068
|
-
total: totalResources,
|
|
3069
3086
|
isValidating: true
|
|
3070
3087
|
});
|
|
3071
3088
|
const validatedJob = await pollJobUntilValidated({
|
|
@@ -3079,7 +3096,6 @@ const useFileUpload = _ref2 => {
|
|
|
3079
3096
|
const processed = job.summary?.total ?? 0;
|
|
3080
3097
|
setValidationProgress({
|
|
3081
3098
|
processed,
|
|
3082
|
-
total: totalResources,
|
|
3083
3099
|
isValidating: true
|
|
3084
3100
|
});
|
|
3085
3101
|
config.onValidationProgress?.(job);
|
|
@@ -3109,7 +3125,6 @@ const useFileUpload = _ref2 => {
|
|
|
3109
3125
|
setIsUploading(false);
|
|
3110
3126
|
setValidationProgress({
|
|
3111
3127
|
processed: 0,
|
|
3112
|
-
total: 0,
|
|
3113
3128
|
isValidating: false
|
|
3114
3129
|
});
|
|
3115
3130
|
config.onSuccess(result);
|
|
@@ -3192,4 +3207,4 @@ const useFileUpload = _ref2 => {
|
|
|
3192
3207
|
};
|
|
3193
3208
|
};
|
|
3194
3209
|
|
|
3195
|
-
export { ActiveDragDropArea, COLUMN_DELIMITERS, CT_API_DOCS_URL, DELIMITERS, DisabledDropArea, DropAreaWrapper, EnabledDropArea, FILE_IMPORT_JOB_POLLING_INTERVAL, FileDropArea, FileDroppedArea, FileIcon, HttpError, IMPORT_LEGACY_MAX_FILE_SIZE_MB, IMPORT_LEGACY_MAX_ROW_COUNT, IMPORT_MAX_FILE_SIZE_MB, IMPORT_MAX_ITEM_COUNT, IMPORT_TAG_KEYS, IMPORT_TAG_VALUES, ImportStates, InfoBox, InvalidResponseError, LockIcon, NoResourcesToExportError, PollingAbortedError, ProjectKeyNotAvailableError, QueryPredicateError, RESOURCE_TYPE_DOCUMENTATION_LINKS, RESOURCE_TYPE_TEMPLATE_DOWNLOAD_LINKS, TAG_KEY_SOURCE_FILE_UPLOAD, UnexpectedColumnError, UnexpectedOperationStateError, UnexpectedResourceTypeError, UploadSeparator, UploadSettings, UploadingModal, allAutomatedImportOperations, allAutomatedImportOperationsResponse, allFileUploadImportOperations, allFileUploadImportOperationsResponse, appendCsvOrJsonExtensionIfAbsent, assertCancelContainerResponse, assertExportOperationsDownloadFileResponse, assertFileImportJob, assertFileImportJobRecordsResponse, assertFileUploadResponse, assertImportContainer, assertImportContainerPagedResponse, assertImportOperationPagedResponse, assertImportSummary, assertListFileImportJobsResponse, assertPaginatedExportOperationResponse, assertProcessFileImportJobResponse, assertProcessFileResponse, assertResourceType, automatedImportContainerKey, automatedImports, cancelImportContainerByKey, checkIfFileUploadImport, convertFileSizeToKB, countJsonFileItems,
|
|
3210
|
+
export { ActiveDragDropArea, COLUMN_DELIMITERS, CT_API_DOCS_URL, DELIMITERS, DisabledDropArea, DropAreaWrapper, EnabledDropArea, FILE_IMPORT_JOB_POLLING_INTERVAL, FileDropArea, FileDroppedArea, FileIcon, HttpError, IMPORT_LEGACY_MAX_FILE_SIZE_MB, IMPORT_LEGACY_MAX_ROW_COUNT, IMPORT_MAX_FILE_SIZE_MB, IMPORT_MAX_ITEM_COUNT, IMPORT_TAG_KEYS, IMPORT_TAG_VALUES, ImportStates, InfoBox, InvalidResponseError, LockIcon, NoResourcesToExportError, PollingAbortedError, ProjectKeyNotAvailableError, QueryPredicateError, RESOURCE_TYPE_DOCUMENTATION_LINKS, RESOURCE_TYPE_TEMPLATE_DOWNLOAD_LINKS, TAG_KEY_SOURCE_FILE_UPLOAD, UnexpectedColumnError, UnexpectedOperationStateError, UnexpectedResourceTypeError, UploadSeparator, UploadSettings, UploadingModal, allAutomatedImportOperations, allAutomatedImportOperationsResponse, allFileUploadImportOperations, allFileUploadImportOperationsResponse, appendCsvOrJsonExtensionIfAbsent, assertCancelContainerResponse, assertExportOperationsDownloadFileResponse, assertFileImportJob, assertFileImportJobRecordsResponse, assertFileUploadResponse, assertImportContainer, assertImportContainerPagedResponse, assertImportOperationPagedResponse, assertImportSummary, assertListFileImportJobsResponse, assertPaginatedExportOperationResponse, assertProcessFileImportJobResponse, assertProcessFileResponse, assertResourceType, automatedImportContainerKey, automatedImports, cancelImportContainerByKey, checkIfFileUploadImport, convertFileSizeToKB, countJsonFileItems, createFileImportJob, createImportContainerForFileUpload, decodeFileNameFromImportContainerKey, deleteFileImportJob, deleteImportContainer, dropAreaStyles, encodeFileNameWithTimestampToContainerKey, exportOperationsCompleted, exportOperationsProcessing, extractErrorDescriptionFromValidationMessage, fetchExportOperations, fetchImportContainerByKey, fetchImportContainerDetails, fetchImportContainers, fetchImportOperations, fetchImportSummaries, fetchImportSummary, fetchUsingXhr, fetcher, fileUploadImportContainerKey, fileUploadMissingKeysResponse, formatErrorCode, formatKeys, formatQueryString, getCreateImportContainerURL, getDeleteImportContainerURL, getExportOperationsURL, getFileImportJob, getFileImportJobByIdURL, getFileImportJobDeleteURL, getFileImportJobFileType, getFileImportJobInfoForContainer, getFileImportJobProcessURL, getFileImportJobRecords, getFileImportJobRecordsURL, getFileImportJobsListURL, getFileImportJobsURL, getFileUploadErrorsCount, getFileUploadURL, getImportContainerByKeyURL, getImportContainerTasksURL, getImportContainersURL, getImportOperationsURL, getImportState, getImportSummaryURL, getMissingRequiredFields, getProccessFileURL, getRowCount, getValidatedColumns, hasImportJobStartedProcessing, hasOwnProperty, hasRequiredFields, hasSingleKeyColumn, importContainers, importStatesMap, importsSummaries, invalidFileImportJobRecordsResponse, invalidFileImportJobValidated, invalidFileUploadResponse, isAbortError, isError, isImportJobInitializing, isImportJobProcessing, isImportJobQueued, isImportJobReady, isImportJobRejected, isImportJobTerminal, isImportJobValidated, isResourceType, listFileImportJobs, manualImports, mapFileUploadErrorsToUploadFileErrorRows, mapFormikErrors, mapUploadFileErrorsResponseToUploadFileErrorRows, pollJobUntilProcessing, pollJobUntilValidated, processFileImportJob, processFileImportJobResponse, processUploadedFile, shouldContinuePollingForImportValidation, successfulAutomatedImportOperations, successfulAutomatedImportOperationsResponse, successfulFileUploadImportOperations, successfulFileUploadImportOperationsResponse, toBytes, toImportApiResourceType, uploadFileForImport, useFetchExportOperations, useFetchFileImportJob, useFetchFileImportJobRecords, useFetchImportContainerDetails, useFetchImportOperations, useFetchImportSummaries, useFileImportJobUpload, useFileUpload, useImportContainerUpload, validFileImportJobProcessing, validFileImportJobQueued, validFileImportJobRecordsResponse, validFileImportJobValidated, validFileUploadResponse, validProcessFileResponse, validateDelimiter };
|
|
@@ -5,3 +5,24 @@ export declare function getFileImportJobRecords({ projectKey, importContainerKey
|
|
|
5
5
|
export declare function processFileImportJob({ projectKey, resourceType, importContainerKey, jobId, action, }: ProcessFileImportJobParameters): Promise<ProcessFileImportJobResponse>;
|
|
6
6
|
export declare function deleteFileImportJob({ projectKey, importContainerKey, jobId, }: DeleteFileImportJobParameters): Promise<void>;
|
|
7
7
|
export declare function listFileImportJobs({ projectKey, importContainerKey, limit, offset, }: ListFileImportJobsParameters): Promise<ListFileImportJobsResponse>;
|
|
8
|
+
export type FileImportJobInfo = {
|
|
9
|
+
total: number;
|
|
10
|
+
isInitializing: boolean;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Gets the file import job info for an import container
|
|
14
|
+
*
|
|
15
|
+
* For the new file import job flow, import operations are created incrementally
|
|
16
|
+
* during the 'initialising' state. The import summary total
|
|
17
|
+
* reflects only the operations created so far, which can be misleading
|
|
18
|
+
*
|
|
19
|
+
* This helper fetches the file import job (if it exists) to get:
|
|
20
|
+
* - The true total from the job summary (known from initial CSV validation)
|
|
21
|
+
* - Whether the job is still initializing (creating import operations)
|
|
22
|
+
*
|
|
23
|
+
* @returns Job info if found, null otherwise
|
|
24
|
+
*/
|
|
25
|
+
export declare function getFileImportJobInfoForContainer({ projectKey, importContainerKey, }: {
|
|
26
|
+
projectKey: string;
|
|
27
|
+
importContainerKey: string;
|
|
28
|
+
}): Promise<FileImportJobInfo | null>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { ImportContainer, ImportContainerPagedResponse } from '@commercetools/importapi-sdk';
|
|
2
2
|
import { ImportStates, type ImportContainerQueryParams, type CancelContainerResponse, type ImportSummary, type ExtendedImportContainerDraft, type ImportContainerDetails, type ImportSummaries } from "../@types/index.js";
|
|
3
|
-
|
|
3
|
+
type GetImportStateOptions = {
|
|
4
|
+
isJobInitializing?: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function getImportState(importSummary: ImportSummary, options?: GetImportStateOptions): ImportStates;
|
|
4
7
|
export declare function createImportContainerForFileUpload({ importContainerDraft, projectKey, }: {
|
|
5
8
|
importContainerDraft: ExtendedImportContainerDraft;
|
|
6
9
|
projectKey: string;
|
|
@@ -33,3 +36,4 @@ export declare function cancelImportContainerByKey({ projectKey, importContainer
|
|
|
33
36
|
projectKey: string;
|
|
34
37
|
importContainerKey: string;
|
|
35
38
|
}): Promise<CancelContainerResponse>;
|
|
39
|
+
export {};
|
|
@@ -2,7 +2,6 @@ import type { ResourceTypeId } from '@commercetools/importapi-sdk';
|
|
|
2
2
|
import type { ExtendedImportContainerDraft, FileUploadResult, FileImportJob } from "../@types/index.js";
|
|
3
3
|
export type ValidationProgress = {
|
|
4
4
|
processed: number;
|
|
5
|
-
total: number;
|
|
6
5
|
isValidating: boolean;
|
|
7
6
|
};
|
|
8
7
|
export type FileUploadConfig = {
|
|
@@ -93,7 +93,13 @@ export interface ListFileImportJobsParameters {
|
|
|
93
93
|
limit?: number;
|
|
94
94
|
offset?: number;
|
|
95
95
|
}
|
|
96
|
-
export
|
|
96
|
+
export interface ListFileImportJobsResponse {
|
|
97
|
+
results: FileImportJob[];
|
|
98
|
+
total: number;
|
|
99
|
+
limit: number;
|
|
100
|
+
offset: number;
|
|
101
|
+
count: number;
|
|
102
|
+
}
|
|
97
103
|
export declare function assertFileImportJob(maybeJob: unknown): asserts maybeJob is FileImportJob;
|
|
98
104
|
export declare function assertFileImportJobRecordsResponse(maybeRecords: unknown): asserts maybeRecords is FileImportJobRecordsResponse;
|
|
99
105
|
export declare function assertProcessFileImportJobResponse(maybeResponse: unknown): asserts maybeResponse is ProcessFileImportJobResponse;
|
|
@@ -36,14 +36,6 @@ export declare const countJsonFileItems: (file: File) => Promise<{
|
|
|
36
36
|
isValid: true;
|
|
37
37
|
itemsCount: number;
|
|
38
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
39
|
/**
|
|
48
40
|
* Map file upload errors to upload file error rows with unique IDs
|
|
49
41
|
* @param uploadFileErrors Array of file upload errors
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools-frontend-extensions/operations",
|
|
3
|
-
"version": "3.1
|
|
3
|
+
"version": "3.2.1",
|
|
4
4
|
"license": "Proprietary",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
"react-dropzone": "14.3.8"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@commercetools-frontend/actions-global": "24.
|
|
22
|
-
"@commercetools-frontend/application-components": "24.
|
|
23
|
-
"@commercetools-frontend/application-shell": "24.
|
|
24
|
-
"@commercetools-frontend/application-shell-connectors": "24.
|
|
25
|
-
"@commercetools-frontend/constants": "24.
|
|
26
|
-
"@commercetools-frontend/jest-preset-mc-app": "24.
|
|
27
|
-
"@commercetools-frontend/permissions": "24.
|
|
28
|
-
"@commercetools-frontend/sentry": "24.
|
|
21
|
+
"@commercetools-frontend/actions-global": "24.13.0",
|
|
22
|
+
"@commercetools-frontend/application-components": "24.13.0",
|
|
23
|
+
"@commercetools-frontend/application-shell": "24.13.0",
|
|
24
|
+
"@commercetools-frontend/application-shell-connectors": "24.13.0",
|
|
25
|
+
"@commercetools-frontend/constants": "24.13.0",
|
|
26
|
+
"@commercetools-frontend/jest-preset-mc-app": "24.13.0",
|
|
27
|
+
"@commercetools-frontend/permissions": "24.13.0",
|
|
28
|
+
"@commercetools-frontend/sentry": "24.13.0",
|
|
29
29
|
"@commercetools-frontend/ui-kit": "20.3.0",
|
|
30
30
|
"@emotion/react": "11.14.0",
|
|
31
31
|
"@emotion/styled": "11.14.1",
|
|
@@ -217,3 +217,55 @@ export async function listFileImportJobs({
|
|
|
217
217
|
assertListFileImportJobsResponse(response)
|
|
218
218
|
return response
|
|
219
219
|
}
|
|
220
|
+
|
|
221
|
+
export type FileImportJobInfo = {
|
|
222
|
+
total: number
|
|
223
|
+
isInitializing: boolean
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Gets the file import job info for an import container
|
|
228
|
+
*
|
|
229
|
+
* For the new file import job flow, import operations are created incrementally
|
|
230
|
+
* during the 'initialising' state. The import summary total
|
|
231
|
+
* reflects only the operations created so far, which can be misleading
|
|
232
|
+
*
|
|
233
|
+
* This helper fetches the file import job (if it exists) to get:
|
|
234
|
+
* - The true total from the job summary (known from initial CSV validation)
|
|
235
|
+
* - Whether the job is still initializing (creating import operations)
|
|
236
|
+
*
|
|
237
|
+
* @returns Job info if found, null otherwise
|
|
238
|
+
*/
|
|
239
|
+
export async function getFileImportJobInfoForContainer({
|
|
240
|
+
projectKey,
|
|
241
|
+
importContainerKey,
|
|
242
|
+
}: {
|
|
243
|
+
projectKey: string
|
|
244
|
+
importContainerKey: string
|
|
245
|
+
}): Promise<FileImportJobInfo | null> {
|
|
246
|
+
try {
|
|
247
|
+
const response = await listFileImportJobs({
|
|
248
|
+
projectKey,
|
|
249
|
+
importContainerKey,
|
|
250
|
+
limit: 1,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
if (
|
|
254
|
+
response.results.length > 0 &&
|
|
255
|
+
response.results[0].summary?.total != null
|
|
256
|
+
) {
|
|
257
|
+
const job = response.results[0]
|
|
258
|
+
const isInitializing = ['processing', 'initialising'].includes(job.state)
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
total: job.summary.total,
|
|
262
|
+
isInitializing,
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return null
|
|
267
|
+
} catch {
|
|
268
|
+
// Job might not exist (old flow)
|
|
269
|
+
return null
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -27,8 +27,16 @@ import {
|
|
|
27
27
|
} from './urls'
|
|
28
28
|
import { checkIfFileUploadImport } from '../@utils'
|
|
29
29
|
import { fetcher } from './fetcher'
|
|
30
|
+
import { getFileImportJobInfoForContainer } from './file-import-jobs'
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
type GetImportStateOptions = {
|
|
33
|
+
isJobInitializing?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getImportState(
|
|
37
|
+
importSummary: ImportSummary,
|
|
38
|
+
options: GetImportStateOptions = {}
|
|
39
|
+
): ImportStates {
|
|
32
40
|
const processing = importSummary.states.processing > 0
|
|
33
41
|
if (processing) return ImportStates.Processing
|
|
34
42
|
|
|
@@ -48,6 +56,11 @@ export function getImportState(importSummary: ImportSummary): ImportStates {
|
|
|
48
56
|
const noRunning = importSummary.total === 0
|
|
49
57
|
if (noRunning) return ImportStates.NoRunningImports
|
|
50
58
|
|
|
59
|
+
// For the new flow: job is actively creating import operations (to show as Processing even if no operations exist yet)
|
|
60
|
+
if (options.isJobInitializing) {
|
|
61
|
+
return ImportStates.Processing
|
|
62
|
+
}
|
|
63
|
+
|
|
51
64
|
const successfullyCompleted =
|
|
52
65
|
importSummary.states.imported === importSummary.total ||
|
|
53
66
|
importSummary.states.deleted === importSummary.total
|
|
@@ -240,13 +253,31 @@ async function importContainerToContainerDetails(
|
|
|
240
253
|
projectKey: string,
|
|
241
254
|
importContainer: ExtendedImportContainer
|
|
242
255
|
): Promise<ImportContainerDetails> {
|
|
243
|
-
|
|
256
|
+
let importSummary = await fetchImportSummary({
|
|
244
257
|
projectKey,
|
|
245
258
|
importContainerKey: importContainer.key,
|
|
246
259
|
})
|
|
247
|
-
const importState = getImportState(importSummary)
|
|
248
260
|
const isFileUploadImport = checkIfFileUploadImport(importContainer.tags)
|
|
249
261
|
|
|
262
|
+
// For the new file import job flow the import operations are created incrementally
|
|
263
|
+
// The import summary total reflects only operations created so far
|
|
264
|
+
// Only override total when job is actively initializing (creating operations)
|
|
265
|
+
let isJobInitializing = false
|
|
266
|
+
if (isFileUploadImport) {
|
|
267
|
+
const jobInfo = await getFileImportJobInfoForContainer({
|
|
268
|
+
projectKey,
|
|
269
|
+
importContainerKey: importContainer.key,
|
|
270
|
+
})
|
|
271
|
+
if (jobInfo !== null) {
|
|
272
|
+
isJobInitializing = jobInfo.isInitializing
|
|
273
|
+
if (isJobInitializing || importSummary.total > 0) {
|
|
274
|
+
importSummary = { ...importSummary, total: jobInfo.total }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const importState = getImportState(importSummary, { isJobInitializing })
|
|
280
|
+
|
|
250
281
|
return {
|
|
251
282
|
importContainer: importContainer,
|
|
252
283
|
importState,
|
|
@@ -56,10 +56,12 @@ export const UploadingModal = ({
|
|
|
56
56
|
onClick={onCancel}
|
|
57
57
|
/>
|
|
58
58
|
</Spacings.Inline>
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
<Spacings.Stack scale="xs">
|
|
60
|
+
<ProgressBar barWidth="scale" height="10" progress={progress} />
|
|
61
|
+
{statusMessage && (
|
|
62
|
+
<Text.Detail tone="tertiary">{statusMessage}</Text.Detail>
|
|
63
|
+
)}
|
|
64
|
+
</Spacings.Stack>
|
|
63
65
|
</Spacings.Stack>
|
|
64
66
|
</InfoDialog>
|
|
65
67
|
)
|
|
@@ -4,11 +4,7 @@ import { useImportContainerUpload } from './use-import-container-upload'
|
|
|
4
4
|
import { useFileImportJobUpload } from './use-file-import-job-upload'
|
|
5
5
|
import { deleteImportContainer } from '../@api'
|
|
6
6
|
import { HttpError, PollingAbortedError } from '../@errors'
|
|
7
|
-
import {
|
|
8
|
-
pollJobUntilValidated,
|
|
9
|
-
pollJobUntilProcessing,
|
|
10
|
-
countUniqueResourcesInCsv,
|
|
11
|
-
} from '../@utils'
|
|
7
|
+
import { pollJobUntilValidated, pollJobUntilProcessing } from '../@utils'
|
|
12
8
|
import type {
|
|
13
9
|
ExtendedImportContainerDraft,
|
|
14
10
|
FileUploadResult,
|
|
@@ -17,7 +13,6 @@ import type {
|
|
|
17
13
|
|
|
18
14
|
export type ValidationProgress = {
|
|
19
15
|
processed: number
|
|
20
|
-
total: number
|
|
21
16
|
isValidating: boolean
|
|
22
17
|
}
|
|
23
18
|
|
|
@@ -67,7 +62,6 @@ export const useFileUpload = ({
|
|
|
67
62
|
const [validationProgress, setValidationProgress] =
|
|
68
63
|
React.useState<ValidationProgress>({
|
|
69
64
|
processed: 0,
|
|
70
|
-
total: 0,
|
|
71
65
|
isValidating: false,
|
|
72
66
|
})
|
|
73
67
|
|
|
@@ -77,7 +71,7 @@ export const useFileUpload = ({
|
|
|
77
71
|
const resetState = React.useCallback(() => {
|
|
78
72
|
setIsUploading(false)
|
|
79
73
|
setProgress(0)
|
|
80
|
-
setValidationProgress({ processed: 0,
|
|
74
|
+
setValidationProgress({ processed: 0, isValidating: false })
|
|
81
75
|
}, [])
|
|
82
76
|
|
|
83
77
|
const upload = React.useCallback(
|
|
@@ -87,10 +81,6 @@ export const useFileUpload = ({
|
|
|
87
81
|
|
|
88
82
|
try {
|
|
89
83
|
if (useJobBasedFlow) {
|
|
90
|
-
const totalResources = config.skipValidationPolling
|
|
91
|
-
? 0
|
|
92
|
-
: await countUniqueResourcesInCsv(config.file)
|
|
93
|
-
|
|
94
84
|
await jobUpload.upload({
|
|
95
85
|
file: config.file,
|
|
96
86
|
resourceType: config.resourceType,
|
|
@@ -136,7 +126,6 @@ export const useFileUpload = ({
|
|
|
136
126
|
try {
|
|
137
127
|
setValidationProgress({
|
|
138
128
|
processed: 0,
|
|
139
|
-
total: totalResources,
|
|
140
129
|
isValidating: true,
|
|
141
130
|
})
|
|
142
131
|
|
|
@@ -151,7 +140,6 @@ export const useFileUpload = ({
|
|
|
151
140
|
const processed = job.summary?.total ?? 0
|
|
152
141
|
setValidationProgress({
|
|
153
142
|
processed,
|
|
154
|
-
total: totalResources,
|
|
155
143
|
isValidating: true,
|
|
156
144
|
})
|
|
157
145
|
config.onValidationProgress?.(job)
|
|
@@ -186,7 +174,6 @@ export const useFileUpload = ({
|
|
|
186
174
|
setIsUploading(false)
|
|
187
175
|
setValidationProgress({
|
|
188
176
|
processed: 0,
|
|
189
|
-
total: 0,
|
|
190
177
|
isValidating: false,
|
|
191
178
|
})
|
|
192
179
|
config.onSuccess(result)
|
|
@@ -120,7 +120,13 @@ export interface ListFileImportJobsParameters {
|
|
|
120
120
|
offset?: number
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
export
|
|
123
|
+
export interface ListFileImportJobsResponse {
|
|
124
|
+
results: FileImportJob[]
|
|
125
|
+
total: number
|
|
126
|
+
limit: number
|
|
127
|
+
offset: number
|
|
128
|
+
count: number
|
|
129
|
+
}
|
|
124
130
|
|
|
125
131
|
export function assertFileImportJob(
|
|
126
132
|
maybeJob: unknown
|
|
@@ -152,15 +158,10 @@ export function assertProcessFileImportJobResponse(
|
|
|
152
158
|
export function assertListFileImportJobsResponse(
|
|
153
159
|
maybeResponse: unknown
|
|
154
160
|
): asserts maybeResponse is ListFileImportJobsResponse {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (!hasRequiredFields(maybeResponse[0], requiredFields)) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
'Invalid List File Import Jobs response: missing required fields'
|
|
163
|
-
)
|
|
164
|
-
}
|
|
161
|
+
const requiredFields = ['results', 'total', 'limit', 'offset', 'count']
|
|
162
|
+
if (!hasRequiredFields(maybeResponse, requiredFields)) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
'Invalid List File Import Jobs response: missing required fields'
|
|
165
|
+
)
|
|
165
166
|
}
|
|
166
167
|
}
|