Skip to content
Open
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
100 changes: 2 additions & 98 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,100 +1,4 @@
fileignoreconfig:
- filename: pnpm-lock.yaml
checksum: 2e1116588e19b058f0d6a5efa28f54c6a3af11b0f0d8c442ed9866a90b77b713
- filename: packages/contentstack-clone/test/lib/helpers/command-helpers.test.ts
checksum: 32d385a677a76faa54cb3ddfe92fb1f1f53cfab878a78e2792c823a363956bd2
- filename: packages/contentstack-clone/test/commands/stack-clone.test.ts
checksum: 4547ba010cc3c9954de49e2b2bef45288dd070c4ad000b6ae3e9a8c9bb1ad296
- filename: packages/contentstack-asset-management/test/unit/import/workspaces.test.ts
checksum: 67c11d312ba6838659c0b2cefd59d706d4b3c1a9807ed551dcb6d94f4d1d60a3
- filename: packages/contentstack-clone/test/lib/util/clone-handler.branch.test.ts
checksum: db8c090aaada21d3952c5bc382228a0a11cebb63926512a5e2581bdaf5f4c2f6
- filename: packages/contentstack-asset-management/test/unit/import/spaces.test.ts
checksum: d2363470664a6cdeaf59c305fa6307704e48b73f92b7d471433c17528daa5450
- filename: packages/contentstack-asset-management/test/unit/utils/detect-asset-management-export.test.ts
checksum: 1917c51ebf4ba7f94cc440c27341d840911b5c315e250a451732c2d37518db64
- filename: packages/contentstack-bootstrap/test/bootstrap-integration.test.js
checksum: 67579b79fc5c1b4feead8f537e7bdfd14fac58aa93c127f590505e47d26e0eb0
- filename: packages/contentstack-import/test/unit/import/modules/index.test.ts
checksum: 27dd2c5f60f9f4c70f2ebf4c08cf8bcbe2d5e4dc7fe52a53972af3fdb57e0e63
- filename: packages/contentstack-bulk-operations/test/unit/base-bulk-command.test.ts
checksum: 522b91a2d4bb6a73ebe5ab92c0111e74d567c4360470beefd6a7ca8d30a6e311
- filename: packages/contentstack-content-type/tests/core/command.test.ts
checksum: 81da32fc47083a3421f5f1d9c04149ab313cb238d6416d3182f8b6d64dc8827d
- filename: packages/contentstack-content-type/tests/core/contentstack/client.test.ts
checksum: da15b04aa0a7ab0e48f70b847e68c05372943279e51c22f8539b01bb48033183
- filename: packages/contentstack-import/test/unit/import/modules/marketplace-apps.test.ts
checksum: 17b3e6e0f7ce12ddabd688996c72d04dba5245fd300a23b541cf7bdfbbf7ef1a
- filename: packages/contentstack-asset-management/test/unit/import/asset-types.test.ts
checksum: 76aad963f5977059f7632d69a6fb36004229f3455b1618c372579dfa3cbc2d62
- filename: packages/contentstack-asset-management/test/unit/import/assets.test.ts
checksum: f5f0494c9263ebcf7eabff806ff41bd7beed0cb6a0f9a631dd70ffa01524a304
- filename: packages/contentstack-import/test/unit/utils/common-helper.test.ts
checksum: 7a10c4625d666cf705cd9869b74b3bc2dcec94926cbb5f719e4f8dc4f053b2c7
- filename: packages/contentstack-content-type/tests/utils/index.test.ts
checksum: 8510fedf5eb6078c27d6a0d65d8bff4aa2d41b545246aed220d1a6a643188ad8
- filename: packages/contentstack-import/test/unit/utils/interactive.test.ts
checksum: dad20e229e02e847b5e939fdaf987b1a6c9ff4e1529b6d96a51130f7293285a9
- filename: packages/contentstack-asset-management/test/unit/import/base.test.ts
checksum: 53168bda5251300d8dfd0effd470070a5733c37b77abacc846d91e7086c8c022
- filename: packages/contentstack-asset-management/test/unit/import/fields.test.ts
checksum: 9e97001a6c92d213a9f55a1458bf39c0996952467e0b4aabd954ddd8ad1805b9
- filename: packages/contentstack-export-to-csv/test/unit/commands/export-to-csv.test.ts
checksum: cc82f4b0c50783dd42d8d57ba2af1e5c9aa3857399c5abf39bc5dc3fac105658
- filename: packages/contentstack-bulk-operations/test/unit/utils/bulk-operation-log-handler.test.ts
checksum: 8698f77bbb828df10ddc3a94726076a262fbf47d2468233c7e10e66cac42f97d
- filename: packages/contentstack-import/test/unit/utils/login-handler.test.ts
checksum: a60f46b4d79ccdbeecfed27ffffbc97a44d6753f36ee74b68b725c8b20bbb2eb
- filename: packages/contentstack-export-to-csv/test/unit/utils/error-handler.test.ts
checksum: cef8f515ab6c0441f67748693473bf1499c2d54945aab1b89baaad37be8af2d9
- filename: packages/contentstack-export-to-csv/test/unit/utils/teams-export.functional.test.ts
checksum: bcf70dbb201a0a1755f0b8f59550aae95a3e1d03700959eee7e8d415f9647746
- filename: packages/contentstack-export/test/unit/utils/marketplace-app-helper.test.ts
checksum: 5648d90b0f7dc33a5b020324db5218707861ebd7201802f7fef00f1c03c5ce82
- filename: packages/contentstack-export/test/unit/export/module-exporter.test.ts
checksum: 485bbd175cc95aff6c1c3c6b2904f92c3d4ea1272a056158a901da3eff9692d6
- filename: packages/contentstack-import-setup/test/unit/common-helper.test.ts
checksum: ccbd8209d342bf651c2c38326cba13bc3e5c2af4200d7110fdd7c3de4824d21d
- filename: packages/contentstack-export/test/unit/utils/common-helper.test.ts
checksum: a4bfba19846e1075821521a202d856203f02a6bc0e5ec4f51b5265801cb4ddd9
- filename: packages/contentstack-import/test/unit/import/modules/assets.test.ts
checksum: 259d5c325ec7de08d0f45099df9d3eb7c97374691c1c4d281221e112b9097191
- filename: packages/contentstack-export/test/unit/export/modules/marketplace-apps.test.ts
checksum: 07eef22a3c471e8da12b1e7e51d45b76410725c4c160a0f35432d9aac5916974
- filename: packages/contentstack-export/test/unit/utils/interactive.test.ts
checksum: 0ea47db305f576c8c9da8cb34ae915f6b01992208ff1c4380cb7ffd93f538e5d
- filename: packages/contentstack-bulk-operations/test/unit/utils/revert-retry-handler.test.ts
checksum: c34a4bdea186f4ce48171d90b8ab0f1c94162e441acb23e3a0e00cb9f9f9255f
- filename: packages/contentstack-bulk-operations/test/unit/utils/config-builder.test.ts
checksum: e5891257999e9e940956ca77f5a7834be35d259edccd41c06f77736194ad7454
- filename: packages/contentstack-migrate-rte/test/commands/json-migration.test.js
checksum: 0d18641a32f7cf33d013a5a7c447c1067ba77022513b42304a727f873c741e99
- filename: packages/contentstack-seed/test/commands/cm/stacks/seed.test.ts
checksum: e469a60f09a9a5fd3e2ce80dd6df8bf4f2c82acad1afebde00f458afa26d36c9
- filename: packages/contentstack-migrate-rte/test/utils/index.js
checksum: f67235e607a446e5b2dd624046966a1ac5af7bce128aa080ec3c420c48b8bddc
- filename: packages/contentstack-query-export/test/unit/common-helper.test.ts
checksum: a0aa399a2ec15760fe25f7e952d64792b55d3c38d2281858a48e64f0f9081344
- filename: packages/contentstack-seed/test/seed/contentstack/client.test.ts
checksum: 63bd3325e2717e5653f12e4afa362938e58eeaad5fbe07c1d41e2bd15e3451bd
- filename: packages/contentstack-import/test/unit/commands/cm/stacks/import.test.ts
checksum: d241111877a997f00702eaf171c1fde53a075a9c8f7971a8e6b1577c1c83279d
- filename: packages/contentstack-import/test/unit/import/modules/base-class.test.ts
checksum: 40c504d508d40b6416ccedb5a323365dd97f313b1dcc885e4fec3639e807b383
- filename: packages/contentstack-import/test/unit/import/modules/content-types.test.ts
checksum: 662cb42217c12af2b4dad5d1355858095c5df062e42c89e09b2680fdbee80699
- filename: packages/contentstack-import/test/unit/import/modules/entries.test.ts
checksum: a7311e3077fee52bfd23c46d51ebe9ce58d202dd735514a44ec230615f4cd917
- filename: packages/contentstack-import/test/unit/import/modules/global-fields.test.ts
checksum: 2b4e0d5d11f825d21d45569f681acd886c5f84717873b26998b4dab2e91bb439
- filename: packages/contentstack-seed/test/seed/interactive.test.ts
checksum: 8364557eb69b9a297281f868420c13c5b61b26b79bc2ceb0a9b548cfb0365d6d
- filename: packages/contentstack-variants/test/unit/export/variant-entries.test.ts
checksum: dd7018cd5cc8b5fab73004b2968afed7ce33abec0b20e1b17f15f2be23b117db
- filename: packages/contentstack-import-setup/test/unit/login-handler.test.ts
checksum: 2df43be75a96e1a3a00dd256628e191909aa9d7f192b672b56cb3772e074958c
- filename: packages/contentstack-export/test/unit/utils/export-config-handler.test.ts
checksum: f612661a8b6784b20ea62794b35192b340a34d8424a54a1a2cffec1878ab7528
- filename: packages/contentstack-import/test/unit/utils/marketplace-app-helper.test.ts
checksum: de694e861560c4c242100eaf17a6d8e230247b99f94e4b428e55ef065084c10c
- filename: packages/contentstack-asset-management/src/utils/cs-assets-api-adapter.ts
checksum: 86f16cef82b891d0946871a5b48726bb2592dc8c16de4c878baf39e5a0a505f7
version: ""
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const FALLBACK_AM_API_CONCURRENCY = 5;
export const DEFAULT_AM_API_CONCURRENCY = FALLBACK_AM_API_CONCURRENCY;
export const FALLBACK_AM_API_PAGE_SIZE = 100;
export const FALLBACK_AM_API_FETCH_CONCURRENCY = 5;
/** Max assets/uids the CS Assets bulk delete/move endpoints accept per request. */
export const CS_ASSETS_BULK_MUTATE_MAX_ITEMS = 100;

/** Fallback strip lists when import options omit `fieldsImportInvalidKeys` / `assetTypesImportInvalidKeys`. */
export const FALLBACK_FIELDS_IMPORT_INVALID_KEYS = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,45 @@ export type BulkDeleteAssetItem = { uid: string; locale: string };

export type BulkDeleteAssetsPayload = { assets: BulkDeleteAssetItem[] };

/** One failed batch when a bulk mutate is split across multiple ≤100-item requests. */
export type BulkMutateFailure = {
batchIndex: number;
count: number;
status?: number;
error: string;
/** Asset uids in the failed batch, so callers can re-run just the failures. */
uids: string[];
};

/** Raw response of a single bulk-mutate request (one ≤100-item POST) as returned by the API. */
export type CsAssetsMutateBatchResponse = { notice?: string; job_id?: string };

/** Aggregate result of a bulk delete, combining every dispatched ≤100-item batch. */
export type BulkDeleteAssetsResponse = {
/** First notice returned; a human-facing message, safe to surface as the summary line. */
notice?: string;
job_id?: string;
/** One submitted job id, for a short summary line; `job_ids` holds all of them (batches
* run concurrently, so this is whichever completed first — not a stable "first batch"). */
primaryJobId?: string;
notices?: string[];
job_ids?: string[];
failures?: BulkMutateFailure[];
batchesTotal?: number;
batchesSucceeded?: number;
};

export type BulkMoveAssetsPayload = {
asset_uids: string[];
target_folder_uid: string;
};

/** Aggregate result of a bulk move (sync; no job ids). */
export type BulkMoveAssetsResponse = {
notice?: string;
notices?: string[];
failures?: BulkMutateFailure[];
batchesTotal?: number;
batchesSucceeded?: number;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import chunk from 'lodash/chunk';
import { HttpClient, log, authenticationHandler, handleAndLogError } from '@contentstack/cli-utilities';

import { withRetry, RetryableHttpError, isRetryableStatus, parseRetryAfterMs } from './retry';
import { FALLBACK_AM_API_FETCH_CONCURRENCY, FALLBACK_AM_API_PAGE_SIZE } from '../constants/index';
import {
CS_ASSETS_BULK_MUTATE_MAX_ITEMS,
FALLBACK_AM_API_CONCURRENCY,
FALLBACK_AM_API_FETCH_CONCURRENCY,
FALLBACK_AM_API_PAGE_SIZE,
} from '../constants/index';

import type {
CSAssetsAPIConfig,
Expand All @@ -13,6 +18,8 @@ import type {
BulkDeleteAssetsResponse,
BulkMoveAssetsPayload,
BulkMoveAssetsResponse,
BulkMutateFailure,
CsAssetsMutateBatchResponse,
CreateAssetMetadata,
CreateAssetTypePayload,
CreateFieldPayload,
Expand Down Expand Up @@ -91,6 +98,21 @@ export type CustomPromiseHandlerInput = {

export type CustomPromiseHandler = (input: CustomPromiseHandlerInput) => Promise<any>;

/**
* Error thrown by {@link CSAssetsAdapter.postJson} for a failed POST. Carries the HTTP
* `status` (undefined for network/transport failures) so callers can classify failures
* without parsing the message string.
*/
export class CsAssetsPostError extends Error {
constructor(
message: string,
public readonly status?: number,
) {
super(message);
this.name = 'CsAssetsPostError';
}
}

export class CSAssetsAdapter implements ICSAssetsAdapter {
private readonly config: CSAssetsAPIConfig;
private readonly apiClient: HttpClient;
Expand Down Expand Up @@ -607,10 +629,11 @@ export class CSAssetsAdapter implements ICSAssetsAdapter {
}
const text = await response.text().catch(() => '');
const bodySnippet = this.formatResponseBodyForError(text);
throw new Error(
throw new CsAssetsPostError(
`CS Assets API POST failed: status ${response.status} path ${path}${
bodySnippet ? `\nResponse: ${bodySnippet}` : ''
}`,
response.status,
);
}
return response.json() as Promise<T>;
Expand All @@ -627,12 +650,17 @@ export class CSAssetsAdapter implements ICSAssetsAdapter {
: await doPost();
} catch (error) {
if (error instanceof RetryableHttpError) {
throw new Error(`CS Assets API POST failed: path ${path} (status ${error.status ?? 'network'}) - ${error.message}`);
throw new CsAssetsPostError(
`CS Assets API POST failed: path ${path} (status ${error.status ?? 'network'}) - ${error.message}`,
error.status,
);
}
if (error instanceof Error && error.message.includes('CS Assets API POST failed')) {
if (error instanceof CsAssetsPostError) {
throw error;
}
throw new Error(`CS Assets API POST failed: path ${path} - ${error instanceof Error ? error.message : String(error)}`);
throw new CsAssetsPostError(
`CS Assets API POST failed: path ${path} - ${error instanceof Error ? error.message : String(error)}`,
);
}
}

Expand Down Expand Up @@ -737,18 +765,76 @@ export class CSAssetsAdapter implements ICSAssetsAdapter {
payload: BulkDeleteAssetsPayload,
): Promise<BulkDeleteAssetsResponse> {
const path = `/api/spaces/${encodeURIComponent(spaceUid)}/assets/bulk/delete?workspace=${encodeURIComponent(workspaceUid)}`;
return this.postJson<BulkDeleteAssetsResponse>(path, payload, { space_key: spaceUid });
const bodies = chunk(payload.assets, CS_ASSETS_BULK_MUTATE_MAX_ITEMS).map((assets) => ({ assets }));
const { notices, jobIds, failures, batchesTotal } = await this.dispatchBulkMutateBatches(spaceUid, path, bodies);
return {
notice: notices[0],
primaryJobId: jobIds[0],
notices,
job_ids: jobIds,
failures,
batchesTotal,
batchesSucceeded: batchesTotal - failures.length,
};
}

/**
* POST /api/spaces/{spaceUid}/assets/bulk-move — move assets into a folder.
* Split into ≤{@link CS_ASSETS_BULK_MUTATE_MAX_ITEMS}-item requests (same cap as delete).
*/
async bulkMoveAssets(
spaceUid: string,
workspaceUid: string = 'main',
payload: BulkMoveAssetsPayload,
): Promise<BulkMoveAssetsResponse> {
const path = `/api/spaces/${encodeURIComponent(spaceUid)}/assets/bulk-move?workspace=${encodeURIComponent(workspaceUid)}`;
return this.postJson<BulkMoveAssetsResponse>(path, payload, { space_key: spaceUid });
const bodies = chunk(payload.asset_uids, CS_ASSETS_BULK_MUTATE_MAX_ITEMS).map((asset_uids) => ({
asset_uids,
target_folder_uid: payload.target_folder_uid,
}));
const { notices, failures, batchesTotal } = await this.dispatchBulkMutateBatches(spaceUid, path, bodies);
return {
notice: notices[0],
notices,
failures,
batchesTotal,
batchesSucceeded: batchesTotal - failures.length,
};
}

/**
* Dispatch pre-chunked bulk-mutate request bodies (each already ≤100 items) through
* {@link postJson} with bounded concurrency via {@link makeConcurrentCall}. A batch
* failure is collected — never rethrown — because the CS Assets bulk endpoints commit
* each request independently, so earlier batches are already applied server-side and
* callers must be able to report partial outcomes. `postJson` retries transient
* (429/5xx) failures; 4xx like the 422 item-cap are not retried.
*/
private async dispatchBulkMutateBatches(
spaceUid: string,
path: string,
bodies: unknown[],
): Promise<{ notices: string[]; jobIds: string[]; failures: BulkMutateFailure[]; batchesTotal: number }> {
const notices: string[] = [];
const jobIds: string[] = [];
const failures: BulkMutateFailure[] = [];
const apiBatches = chunk(bodies, FALLBACK_AM_API_CONCURRENCY);

await this.makeConcurrentCall({ module: `bulk-mutate ${path}`, apiBatches }, async ({ element, batchIndex, index }) => {
const globalIndex = batchIndex * FALLBACK_AM_API_CONCURRENCY + index;
const body = element as { assets?: { uid: string }[]; asset_uids?: string[] };
const uids = body.assets ? body.assets.map((a) => a.uid) : (body.asset_uids ?? []);
try {
const r = await this.postJson<CsAssetsMutateBatchResponse>(path, body, { space_key: spaceUid }, { retry: true });
if (typeof r.notice === 'string') notices.push(r.notice);
if (typeof r.job_id === 'string') jobIds.push(r.job_id);
} catch (e) {
const status = e instanceof CsAssetsPostError ? e.status : undefined;
const message = e instanceof Error ? e.message : String(e);
failures.push({ batchIndex: globalIndex, count: uids.length, status, error: message, uids });
}
});

return { notices, jobIds, failures, batchesTotal: bodies.length };
}
}
Loading