Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
fileignoreconfig:

- filename: pnpm-lock.yaml
checksum: c3bcd614567a430c43ef8c3aa5a4ea2e006486137ba8397d58f01de89b858a1e
- filename: packages/contentstack-seed/tests/importer.test.ts
checksum: cf452b6bc7d3cfcef493ee64e1f5a4e97ac841292ff8ba3515375fc4e8ed049c
- filename: packages/contentstack-variants/test/unit/import/variant-entries.test.ts
checksum: 6aca150cf9656d27acb1811f478d298668ec28f610b2d4820a6d5115612d52c0
- filename: packages/contentstack-export/src/config/index.ts
checksum: 6fa4bba2174bbf33f5611098f49a02bf2fc789f59634e99be58de7e370f5fcd3
- filename: packages/contentstack-branches/test/unit/helpers/stub-auth.ts
checksum: 8cafd5994d3ec13ba9af74c80b330bfd14721ea4e0359b456598964a6c2913ce
- filename: packages/contentstack-seed/tests/contentstack.test.ts
checksum: 04196568bf29f4693e968079867e87d96219b022bfee0f4697dce1b9bc161ac3
- filename: packages/contentstack-export/src/utils/export-config-handler.ts
checksum: 362c1042a70958b52e73a7a3096152cf0259c55095c8b2c6f308b8f5fb90614e
- filename: packages/contentstack-import/src/utils/import-config-handler.ts
checksum: f310186b5ea1fc756bd200dbe01967bc07e5490dc5ed17018f0ca4b35f0fd86f
checksum: 31e333d6769adbaae042c92ea0930fab168a0e06fc1bda406d49fd1042a7a9c7
version: '1.0'
2 changes: 1 addition & 1 deletion packages/contentstack-apps-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/apps-cli",
"version": "1.7.2",
"version": "1.7.3",
"description": "App ClI",
"author": "Contentstack CLI",
"homepage": "https://github.com/contentstack/cli-plugins/tree/main/packages/contentstack-apps-cli",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-audit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/cli-audit",
"version": "1.19.5",
"version": "1.20.0",
"description": "Contentstack audit plugin",
"author": "Contentstack CLI",
"homepage": "https://github.com/contentstack/cli",
Expand Down
4 changes: 2 additions & 2 deletions packages/contentstack-bootstrap/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-bootstrap",
"description": "Bootstrap contentstack apps",
"version": "1.19.7",
"version": "1.19.8",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"scripts": {
Expand All @@ -17,7 +17,7 @@
"lint": "eslint \"src/**/*.ts\""
},
"dependencies": {
"@contentstack/cli-cm-seed": "~1.15.8",
"@contentstack/cli-cm-seed": "~1.15.9",
"@contentstack/cli-command": "~1.8.5",
"@contentstack/cli-config": "~1.21.0",
"@contentstack/cli-utilities": "~1.19.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-branches/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-branches",
"description": "Contentstack CLI plugin to do branches operations",
"version": "1.8.3",
"version": "1.8.4",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-bulk-operations/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/cli-bulk-operations",
"version": "1.2.2",
"version": "1.2.3",
"description": "Contentstack CLI plugin for bulk operations",
"author": "Contentstack CLI",
"homepage": "https://github.com/contentstack/cli-plugins/tree/main/packages/contentstack-bulk-operations",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-bulk-publish/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-bulk-publish",
"description": "Contentstack CLI plugin for bulk publish actions",
"version": "1.12.1",
"version": "1.12.2",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-cli-cm-regex-validate/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-regex-validate",
"description": "Validate Fields with Regex Property of Content Type and Global Field in a Stack",
"version": "1.0.1",
"version": "1.0.2",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli-cm-regex-validate/issues",
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-cli-tsgen/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "contentstack-cli-tsgen",
"description": "Generate TypeScript typings from a Stack.",
"version": "4.10.1",
"version": "4.10.2",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli-plugins/issues",
"dependencies": {
Expand Down
6 changes: 3 additions & 3 deletions packages/contentstack-clone/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@contentstack/cli-cm-clone",
"description": "Contentstack stack clone plugin",
"version": "1.21.9",
"version": "1.21.10",
"author": "Contentstack",
"bugs": "https://github.com/rohitmishra209/cli-cm-clone/issues",
"dependencies": {
"@colors/colors": "^1.6.0",
"@contentstack/cli-cm-export": "~1.25.3",
"@contentstack/cli-cm-import": "~1.33.5",
"@contentstack/cli-cm-export": "~1.26.0",
"@contentstack/cli-cm-import": "~1.34.0",
"@contentstack/cli-command": "~1.8.5",
"@contentstack/cli-utilities": "~1.19.0",
"@oclif/core": "^4.11.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-content-type/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "contentstack-cli-content-type",
"description": "Retrieve information about Content Types in a Stack.",
"version": "1.5.2",
"version": "1.5.3",
"author": "Contentstack Developer",
"bugs": "https://github.com/contentstack/cli-plugins/issues",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-export-to-csv/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-export-to-csv",
"description": "Export entries, taxonomies, terms, or organization users to CSV",
"version": "1.12.6",
"version": "1.12.7",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
Expand Down
2 changes: 2 additions & 0 deletions packages/contentstack-export/messages/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"ASSET_QUERY_FAILED": "Failed to query asset data from the API",
"ASSET_VERSIONED_QUERY_FAILED": "Failed to query versioned asset data from the API",
"ASSET_COUNT_QUERY_FAILED": "Failed to retrieve total asset count",
"ASSET_SCAN_SKIPPED": "Skipping download of asset '%s' (UID: %s) — scan status: %s",
"ASSET_SCAN_SKIP_SUMMARY": "%s asset(s) skipped due to a non-clean scan status (pending/quarantined). Re-run the export once scanning completes to download them.",

"CONTENT_TYPE_EXPORT_COMPLETE": "Content types exported successfully",
"CONTENT_TYPE_NO_TYPES": "No content types found",
Expand Down
6 changes: 3 additions & 3 deletions packages/contentstack-export/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@contentstack/cli-cm-export",
"description": "Contentstack CLI plugin to export content from stack",
"version": "1.25.3",
"version": "1.26.0",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
"@contentstack/cli-command": "~1.8.5",
"@contentstack/cli-utilities": "~1.19.0",
"@contentstack/cli-variants": "~1.6.0",
"@contentstack/cli-variants": "~1.6.1",
"@oclif/core": "^4.11.4",
"async": "^3.2.6",
"big-json": "^3.2.0",
Expand Down Expand Up @@ -97,4 +97,4 @@
"planProtectedFeatures": ["assetsScan"]
},
"repository": "https://github.com/contentstack/cli"
}
}
28 changes: 26 additions & 2 deletions packages/contentstack-export/src/export/modules/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export default class ExportAssets extends BaseClass {
const queryParam = {
...this.commonQueryParam,
include_publish_details: true,
include_asset_scan_status: true,
except: { BASE: this.assetConfig.invalidKeys },
};
this.applyQueryFilters(queryParam, 'assets');
Expand Down Expand Up @@ -168,7 +169,10 @@ export default class ExportAssets extends BaseClass {
indexFileName: 'assets.json',
basePath: this.assetsRootPath,
chunkFileSize: this.assetConfig.chunkFileSize,
metaPickKeys: merge(['uid', 'url', 'filename', 'parent_uid'], this.assetConfig.assetsMetaKeys),
metaPickKeys: merge(
['uid', 'url', 'filename', 'parent_uid', '_asset_scan_status'],
this.assetConfig.assetsMetaKeys,
),
});
}
if (!isEmpty(items)) {
Expand Down Expand Up @@ -204,6 +208,7 @@ export default class ExportAssets extends BaseClass {
const queryParam = {
...this.commonQueryParam,
include_publish_details: true,
include_asset_scan_status: true,
except: { BASE: this.assetConfig.invalidKeys },
};

Expand Down Expand Up @@ -243,7 +248,10 @@ export default class ExportAssets extends BaseClass {
indexFileName: 'versioned-assets.json',
chunkFileSize: this.assetConfig.chunkFileSize,
basePath: pResolve(this.assetsRootPath, 'versions'),
metaPickKeys: merge(['uid', 'url', 'filename', '_version', 'parent_uid'], this.assetConfig.assetsMetaKeys),
metaPickKeys: merge(
['uid', 'url', 'filename', '_version', 'parent_uid', '_asset_scan_status'],
this.assetConfig.assetsMetaKeys,
),
});
}
if (!isEmpty(response)) {
Expand Down Expand Up @@ -329,6 +337,19 @@ export default class ExportAssets extends BaseClass {
listOfAssets = uniqBy(listOfAssets, 'url');
log.debug(`Total unique assets to download: ${listOfAssets.length}`, this.exportConfig.context);

const isNotClean = (asset: any) => asset._asset_scan_status && asset._asset_scan_status !== 'clean';
const skippedAssets = filter(listOfAssets, isNotClean);
listOfAssets = filter(listOfAssets, (asset: any) => !isNotClean(asset));

if (!isEmpty(skippedAssets)) {
for (const asset of skippedAssets) {
log.warn(
messageHandler.parse('ASSET_SCAN_SKIPPED', asset.filename, asset.uid, asset._asset_scan_status),
this.exportConfig.context,
);
}
}

const apiBatches: Array<any> = chunk(listOfAssets, this.assetConfig.downloadLimit);
const downloadedAssetsDirs = await getDirectories(pResolve(this.assetsRootPath, 'files'));

Expand Down Expand Up @@ -413,6 +434,9 @@ export default class ExportAssets extends BaseClass {
promisifyHandler,
).then(() => {
log.success(messageHandler.parse('ASSET_DOWNLOAD_COMPLETE'), this.exportConfig.context);
if (!isEmpty(skippedAssets)) {
log.warn(messageHandler.parse('ASSET_SCAN_SKIP_SUMMARY', skippedAssets.length), this.exportConfig.context);
}
});
}
}
56 changes: 46 additions & 10 deletions packages/contentstack-export/src/export/modules/base-class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import chunk from 'lodash/chunk';
import isEmpty from 'lodash/isEmpty';
import entries from 'lodash/entries';
import isEqual from 'lodash/isEqual';
import { log } from '@contentstack/cli-utilities';
import { log, handleAndLogError } from '@contentstack/cli-utilities';

import { ExportConfig, ModuleClassParams } from '../../types';

Expand Down Expand Up @@ -115,7 +115,16 @@ export default abstract class BaseClass {
}

/* eslint-disable no-await-in-loop */
await Promise.allSettled(allPromise);
const settledResults = await Promise.allSettled(allPromise);
settledResults.forEach((result) => {
if (result.status === 'rejected') {
handleAndLogError(
result.reason,
{ ...this.exportConfig.context },
`Unhandled rejection in '${module}' batch ${batchNo}`,
);
}
});
/* eslint-disable no-await-in-loop */
await this.logMsgAndWaitIfRequired(module, start, batchNo);

Expand Down Expand Up @@ -150,6 +159,30 @@ export default abstract class BaseClass {
if (exeTime < 1000) await this.delay(1000 - exeTime);
}

/**
* Wraps a caller-supplied resolve/reject callback so that if the callback itself throws
* (e.g. a bug while writing/logging the result), the failure is caught right here and
* always recorded via handleAndLogError, instead of turning into a rejected promise that
* a caller further up the chain may or may not notice.
*/
private guardCallback(
callback: (value: any) => void,
moduleName: ApiModuleType,
context: Record<string, any>,
): (value: any) => void {
return (value: any) => {
try {
callback(value);
} catch (error) {
handleAndLogError(
error,
{ ...this.exportConfig.context, ...context },
`Unhandled error while processing '${moduleName}' API result`,
);
}
};
}

/**
* @method makeAPICall
* @param {Record<string, any>} options - Api related params
Expand All @@ -160,32 +193,35 @@ export default abstract class BaseClass {
{ module: moduleName, reject, resolve, url = '', uid = '', additionalInfo, queryParam = {} }: ApiOptions,
isLastRequest = false,
): Promise<any> {
const safeResolve = this.guardCallback(resolve, moduleName, { uid, additionalInfo });
const safeReject = this.guardCallback(reject, moduleName, { uid, additionalInfo });

switch (moduleName) {
case 'asset':
return this.stack
.asset(uid)
.fetch(queryParam)
.then((response: any) => resolve({ response, isLastRequest, additionalInfo }))
.catch((error: Error) => reject({ error, isLastRequest, additionalInfo }));
.then((response: any) => safeResolve({ response, isLastRequest, additionalInfo }))
.catch((error: Error) => safeReject({ error, isLastRequest, additionalInfo }));
case 'assets':
return this.stack
.asset()
.query(queryParam)
.find()
.then((response: any) => resolve({ response, isLastRequest, additionalInfo }))
.catch((error: Error) => reject({ error, isLastRequest, additionalInfo }));
.then((response: any) => safeResolve({ response, isLastRequest, additionalInfo }))
.catch((error: Error) => safeReject({ error, isLastRequest, additionalInfo }));
case 'download-asset':
return this.stack
.asset()
.download({ url, responseType: 'stream' })
.then((response: any) => resolve({ response, isLastRequest, additionalInfo }))
.catch((error: any) => reject({ error, isLastRequest, additionalInfo }));
.then((response: any) => safeResolve({ response, isLastRequest, additionalInfo }))
.catch((error: any) => safeReject({ error, isLastRequest, additionalInfo }));
case 'export-taxonomy':
return this.stack
.taxonomy(uid)
.export(queryParam)
.then((response: any) => resolve({ response, uid }))
.catch((error: any) => reject({ error, uid }));
.then((response: any) => safeResolve({ response, uid }))
.catch((error: any) => safeReject({ error, uid }));
default:
return Promise.resolve();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,33 @@ describe('ExportAssets', () => {

expect(makeConcurrentCallStub.called).to.be.true;
});

it('should skip assets with a non-clean scan status and download the rest', async () => {
getPlainMetaStub.returns({
'file-1': [
{ uid: 'clean-1', url: 'https://test.io/assets/clean-1.jpeg', filename: 'clean-1.jpeg', _asset_scan_status: 'clean' },
{ uid: 'pending-1', url: 'https://test.io/assets/pending-1.zip', filename: 'pending-1.zip', _asset_scan_status: 'pending' },
{ uid: 'quarantined-1', url: 'https://test.io/assets/quarantined-1.zip', filename: 'quarantined-1.zip', _asset_scan_status: 'quarantined' },
],
});

await exportAssets.downloadAssets();

expect(makeConcurrentCallStub.called).to.be.true;
// Only the 'clean' asset should be handed off for download; pending/quarantined are skipped.
expect(makeConcurrentCallStub.firstCall.args[0].totalCount).to.equal(1);
});

it('should download assets with no scan status field (stacks without asset scanning enabled)', async () => {
getPlainMetaStub.returns({
'file-1': [{ uid: 'legacy-1', url: 'https://test.io/assets/legacy-1.jpeg', filename: 'legacy-1.jpeg' }],
});

await exportAssets.downloadAssets();

// Missing _asset_scan_status must not be treated as non-clean, or every export would break.
expect(makeConcurrentCallStub.firstCall.args[0].totalCount).to.equal(1);
});
});

describe('Edge Cases', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-external-migrate/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/cli-external-migrate",
"version": "1.0.0-alpha.5",
"version": "1.0.0-alpha.6",
"description": "Migrate from external source to Contentstack",
"author": "Contentstack",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/contentstack-import-setup/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@contentstack/cli-cm-import-setup",
"description": "Contentstack CLI plugin to setup the mappers and configurations for the import command",
"version": "1.8.6",
"version": "1.8.7",
"author": "Contentstack",
"bugs": "https://github.com/contentstack/cli/issues",
"dependencies": {
Expand Down
Loading
Loading