Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,12 @@ fileignoreconfig:
checksum: 35451ca4c03359b06531a8461091f4a3a45c4dea1f8853081fb53e4ce28c1cc3
- filename: packages/contentstack-bulk-operations/test/unit/base-bulk-command.test.ts
checksum: ca699a9d73757d44ded4d9d27beb4f460f911a7c88a14551a9402a3ac0c69873
- filename: packages/contentstack-external-migrate/src/lib/create-stack.ts
checksum: 68a9510db6f2746ac5006c091d276c1ba619a9e15c76a3edae3967e4f9c2dd4e
- filename: packages/contentstack-external-migrate/test/lib/create-stack.test.ts
checksum: 45af4e45e4baadf7e418a11a46a6243b8ffabafdbf32d0269d89c5d3db837a13
- filename: CHANGELOG.md
checksum: 88c7e1dee308fa4ae25e7815ead0842b1aac7ed669b02859cc8b25cba879595d
- filename: packages/contentstack-bulk-operations/test/unit/services/taxonomy-service.test.ts
checksum: abc5ac707341760cf59d5b8b1c4e13cf2c79955e2735c33e2db3ec6bc48eddb6
version: ""
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ Please refer to the Contentstack Command-line Interface release notes [here](htt



## @contentstack/cli-bulk-operations
### Version: 2.0.0-beta.5
#### Date: Jul-21-2025
##### Breaking Change:
- Removed the api version flag from `cm:stacks:bulk-entries`. The NRP header value is now hardcoded at the SDK call site, so the flag is no longer needed. Any scripts or CI pipelines that pass this flag must remove it — it will cause an unrecognized-flag error after this release.
##### Fix:
- Force NRP header to version 3.2 on all entry and asset publish/unpublish requests. The header is injected per-call and does not affect other CMA requests.

#### Date: Feb-09-2025
## cli
- Refactor Endpoints Integration using Utils SDK in cli-cm-config v1.9.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ export default class BulkEntries extends BaseBulkCommand {
description: messages.INCLUDE_VARIANTS,
default: false,
}),

'api-version': flags.string({
description: messages.API_VERSION,
default: '3.2',
}),
};

protected resourceType: ResourceType = ResourceType.ENTRY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ export default class BulkTaxonomies extends BaseBulkCommand {
// Multiple locales with a Management token alias
'<%= config.bin %> <%= command.id %> --operation publish --environments staging --locales en-us,fr-fr --taxonomies taxonomy_a -a myAlias',

// Explicit CMA version for taxonomy publish (default is 3.2)
'<%= config.bin %> <%= command.id %> --operation publish --environments development --locales en-us --taxonomies products_tax --api-version 3.2 -k blt123',

// Publish taxonomies on a non-main branch
'<%= config.bin %> <%= command.id %> --operation publish --branch feature --environments development --locales en-us --taxonomies brands_tax -k blt123',
];
Expand All @@ -42,10 +39,6 @@ export default class BulkTaxonomies extends BaseBulkCommand {
taxonomies: flags.string({
description: messages.TAXONOMY_ITEMS,
}),
'api-version': flags.string({
default: '3.2',
description: messages.TAXONOMY_API_VERSION,
}),
} as any;

protected resourceType: ResourceType = ResourceType.TAXONOMY;
Expand Down Expand Up @@ -173,7 +166,6 @@ export default class BulkTaxonomies extends BaseBulkCommand {
throw new Error($t(messages.UNSUPPORTED_OPERATION, { operation: operation ?? 'unknown' }));
}

const apiVersion = this.parsedFlags['api-version'] || '3.2';
const locales = this.bulkOperationConfig.locales || [];
const environments = this.bulkOperationConfig.environments || [];

Expand All @@ -185,8 +177,8 @@ export default class BulkTaxonomies extends BaseBulkCommand {
};
const response =
operation === OperationType.UNPUBLISH
? await taxonomyService.unpublish(payload, apiVersion, this.bulkOperationConfig.branch)
: await taxonomyService.publish(payload, apiVersion, this.bulkOperationConfig.branch);
? await taxonomyService.unpublish(payload, this.bulkOperationConfig.branch)
: await taxonomyService.publish(payload, this.bulkOperationConfig.branch);

const duration = Date.now() - startTime;
const rawJobId = response.job_id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export class OperationExecutor {
private async executeEntryOperation(operation: OperationType, data: EntryPublishData): Promise<any> {
const { uid, content_type, locale, version, publish_details } = data;

const entry = this.stack.contentType(content_type).entry(uid);
const entry = this.stack.contentType(content_type).entry(uid).addHeader('api_version', '3.2');

switch (operation) {
case OperationType.PUBLISH:
Expand Down Expand Up @@ -197,7 +197,7 @@ export class OperationExecutor {
private async executeAssetOperation(operation: OperationType, data: AssetPublishData): Promise<any> {
const { uid, locale, version, publish_details } = data;

const asset = this.stack.asset(uid);
const asset = this.stack.asset(uid).addHeader('api_version', '3.2');

switch (operation) {
case OperationType.PUBLISH:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export interface BulkOperationConfig {

// API configuration
publishMode?: PublishMode;
apiVersion?: string;

// Filtering and selection
branch?: string;
Expand Down Expand Up @@ -215,7 +214,6 @@ export interface CommandFlags {
'source-alias'?: string;

// API configuration
'api-version'?: string;
'publish-mode'?: string;

// Retry, reliability, and operations log
Expand Down
4 changes: 0 additions & 4 deletions packages/contentstack-bulk-operations/src/messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,6 @@ const flagDescriptions = {
'(optional) Revert publish operations from a log folder. Specify the folder path containing success logs. Works similar to retry-failed.',
BULK_OPERATION_FOLDER:
'(optional) Folder path to store operation logs. Creates separate files for success and failed operations. Default: bulk-operation',
API_VERSION:
'Specifies the Content Management API version used for publishing. Use version `3.2` when publishing entries with nested references, otherwise, use the default version 3.2',
TAXONOMY_API_VERSION:
'Content Management API version for taxonomy publish (default: `3.2`; required for the `items` + locales/environments body on POST /v3/taxonomies/publish).',
TAXONOMY_ITEMS:
'Comma-separated taxonomy UIDs to include in the job. If omitted, all taxonomies in the stack (current branch) are included. Example: products_tax,brands_tax',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,13 +245,17 @@ export class BulkOperationService {
}

private prepareAssetBulkPayload(items: AssetPublishData[], operation: OperationType): any {
const assets = items.map((item) => ({
uid: item.uid,
version: item.version,
}));
const seen = new Set<string>();
const assets = items.reduce<Array<{ uid: string; version: number | undefined }>>((acc, item) => {
if (!seen.has(item.uid)) {
seen.add(item.uid);
acc.push({ uid: item.uid, version: item.version });
}
return acc;
}, []);

const environments = items[0]?.publish_details?.map((pd) => pd.environment) || [];
const locales = items[0]?.publish_details?.map((pd) => pd.locale) || [];
const locales = Array.from(new Set(items.map((item) => item.locale)));

return {
assets,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,67 +1,30 @@
import type { ManagementStack, TaxonomyPublishJobResponse, TaxonomyPublishPayload } from '../interfaces';
import { OperationType } from '../interfaces';

const DEFAULT_TAXONOMY_API_VERSION = '3.2';

type TaxonomyPublishWithBranch = (
data: TaxonomyPublishPayload,
apiVersion?: string,
params?: { branch?: string }
) => Promise<TaxonomyPublishJobResponse>;

type TaxonomyOperationApi = {
publish: TaxonomyPublishWithBranch;
unpublish: TaxonomyPublishWithBranch;
};

export class TaxonomyService {
constructor(private stack: ManagementStack) {}

/**
* Publish one or more taxonomies (initiates a publish job).
*/
async publish(
data: TaxonomyPublishPayload,
apiVersion: string = DEFAULT_TAXONOMY_API_VERSION,
branch?: string
): Promise<TaxonomyPublishJobResponse> {
return this.submit(OperationType.PUBLISH, data, apiVersion, branch);
async publish(data: TaxonomyPublishPayload, branch?: string): Promise<TaxonomyPublishJobResponse> {
return this.submit(OperationType.PUBLISH, data, branch);
}

/**
* Unpublish one or more taxonomies (initiates an unpublish job).
*/
async unpublish(
data: TaxonomyPublishPayload,
apiVersion: string = DEFAULT_TAXONOMY_API_VERSION,
branch?: string
): Promise<TaxonomyPublishJobResponse> {
return this.submit(OperationType.UNPUBLISH, data, apiVersion, branch);
async unpublish(data: TaxonomyPublishPayload, branch?: string): Promise<TaxonomyPublishJobResponse> {
return this.submit(OperationType.UNPUBLISH, data, branch);
}

private async submit(
operation: OperationType,
data: TaxonomyPublishPayload,
apiVersion: string,
branch?: string
): Promise<TaxonomyPublishJobResponse> {
const taxonomies = this.stack.taxonomy() as unknown as TaxonomyOperationApi;
const params =
branch && branch !== 'main'
? {
branch,
}
: undefined;
if (operation === OperationType.UNPUBLISH) {
if (params) {
return taxonomies.unpublish(data, apiVersion, params);
}
return taxonomies.unpublish(data, apiVersion);
}
const taxonomyInstance = this.stack.taxonomy() as any;
taxonomyInstance.addHeader('api_version', '3.2');

const params = branch && branch !== 'main' ? { branch } : undefined;

if (params) {
return taxonomies.publish(data, apiVersion, params);
if (operation === OperationType.UNPUBLISH) {
return params ? taxonomyInstance.unpublish(data, undefined, params) : taxonomyInstance.unpublish(data);
}
return taxonomies.publish(data, apiVersion);
return params ? taxonomyInstance.publish(data, undefined, params) : taxonomyInstance.publish(data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,6 @@ function validateConfig(config: BulkOperationConfig): string[] {
}
}

// API version validation
if (config.apiVersion && !['3', '3.2'].includes(config.apiVersion)) {
errors.push(`Invalid API version: ${config.apiVersion}. Supported versions: 3, 3.2`);
}

// Publish mode validation
if (config.publishMode && config.publishMode !== PublishMode.BULK && config.publishMode !== PublishMode.SINGLE) {
errors.push(`Invalid publish mode: ${String(config.publishMode)}. Must be 'bulk' or 'single'`);
Expand Down Expand Up @@ -179,11 +174,6 @@ function validateCommandFlags(flags: CommandFlags): string[] {
}
}

// API version validation
if (flags['api-version'] && !['3', '3.2'].includes(flags['api-version'])) {
errors.push(`Invalid API version: ${flags['api-version']}. Supported versions: 3, 3.2`);
}

// Publish mode validation
if (flags['publish-mode'] && !['bulk', 'single'].includes(flags['publish-mode'])) {
errors.push(`Invalid publish mode: ${flags['publish-mode']}. Must be 'bulk' or 'single'`);
Expand All @@ -199,11 +189,6 @@ function validateCommandFlags(flags: CommandFlags): string[] {
errors.push('--source-alias can only be used with --source-env for cross-publish operations');
}

// Variants require api-version 3.2
if (flags['include-variants'] && flags['api-version'] !== '3.2') {
errors.push('--include-variants requires --api-version 3.2');
}

return errors;
}

Expand Down Expand Up @@ -245,7 +230,6 @@ export function buildConfig(flags: CommandFlags): BulkOperationConfig {
folderUid: flags['folder-uid'],
sourceEnv: flags['source-env'],
publishMode: (flags['publish-mode'] as PublishMode) || PublishMode.BULK,
apiVersion: flags['api-version'] || '3',
branch: flags.branch || 'main',
filter: flags.filter,
maxRetries: flags['max-retries'] || 3,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -863,12 +863,6 @@ describe('BulkEntries Command', () => {
expect(flags['include-variants'].default).to.be.false;
});

it('should have api-version flag', () => {
const flags = BulkEntries.flags;

// api-version default may be '3' or '3.2' depending on configuration
expect(flags['api-version'].default).to.be.oneOf(['3', '3.2']);
});
});

describe('examples validation', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe('OperationExecutor', () => {
contentType: sandbox.stub().returnsThis(),
entry: sandbox.stub().returnsThis(),
asset: sandbox.stub().returnsThis(),
addHeader: sandbox.stub().returnsThis(),
publish: sandbox.stub().resolves({ notice: 'Published successfully' }),
unpublish: sandbox.stub().resolves({ notice: 'Unpublished successfully' }),
};
Expand Down Expand Up @@ -143,6 +144,7 @@ describe('OperationExecutor', () => {

expect(mockStack.contentType.calledWith('blog_post')).to.be.true;
expect(mockStack.entry.calledWith('entry123')).to.be.true;
expect(mockStack.addHeader.calledWith('api_version', '3.2')).to.be.true;
expect(mockStack.publish.called).to.be.true;
});

Expand All @@ -166,6 +168,7 @@ describe('OperationExecutor', () => {

expect(mockStack.contentType.calledWith('blog_post')).to.be.true;
expect(mockStack.entry.calledWith('entry123')).to.be.true;
expect(mockStack.addHeader.calledWith('api_version', '3.2')).to.be.true;
expect(mockStack.unpublish.called).to.be.true;
});

Expand Down Expand Up @@ -211,6 +214,7 @@ describe('OperationExecutor', () => {
await clock.tickAsync(100);

expect(mockStack.asset.calledWith('asset123')).to.be.true;
expect(mockStack.addHeader.calledWith('api_version', '3.2')).to.be.true;
expect(mockStack.publish.called).to.be.true;
});

Expand All @@ -232,6 +236,7 @@ describe('OperationExecutor', () => {
await clock.tickAsync(100);

expect(mockStack.asset.calledWith('asset123')).to.be.true;
expect(mockStack.addHeader.calledWith('api_version', '3.2')).to.be.true;
expect(mockStack.unpublish.called).to.be.true;
});
});
Expand Down
Loading
Loading