Skip to content

Commit 3e6fda5

Browse files
Merge branch 'development' into enh/dx-3701
2 parents cbdaf4b + ab5b003 commit 3e6fda5

File tree

7 files changed

+97
-14
lines changed

7 files changed

+97
-14
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/contentstack-export/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@contentstack/cli-cm-export",
33
"description": "Contentstack CLI plugin to export content from stack",
4-
"version": "1.21.0",
4+
"version": "1.21.1",
55
"author": "Contentstack",
66
"bugs": "https://github.com/contentstack/cli/issues",
77
"dependencies": {

packages/contentstack-export/src/export/modules/taxonomies.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ export default class ExportTaxonomies extends BaseClass {
7373
await this.fetchTaxonomies(masterLocale, true);
7474

7575
if (!this.isLocaleBasedExportSupported) {
76-
log.debug('Localization disabled, falling back to legacy export method', this.exportConfig.context);
76+
this.taxonomies = {};
77+
this.taxonomiesByLocale = {};
78+
79+
// Fetch taxonomies without locale parameter
80+
await this.fetchTaxonomies();
7781
await this.exportTaxonomies();
7882
await this.writeTaxonomiesMetadata();
7983
} else {
@@ -174,15 +178,26 @@ export default class ExportTaxonomies extends BaseClass {
174178
log.debug(`Completed fetching all taxonomies ${localeInfo}`, this.exportConfig.context);
175179
break;
176180
}
177-
} catch (error) {
181+
} catch (error: any) {
178182
log.debug(`Error fetching taxonomies ${localeInfo}`, this.exportConfig.context);
179-
handleAndLogError(error, {
180-
...this.exportConfig.context,
181-
...(localeCode && { locale: localeCode }),
182-
});
183-
if (checkLocaleSupport) {
183+
184+
if (checkLocaleSupport && this.isLocalePlanLimitationError(error)) {
185+
log.debug(
186+
'Taxonomy localization is not included in your plan. Falling back to non-localized export.',
187+
this.exportConfig.context,
188+
);
184189
this.isLocaleBasedExportSupported = false;
190+
} else if (checkLocaleSupport) {
191+
log.debug('Locale-based taxonomy export not supported, will use legacy method', this.exportConfig.context);
192+
this.isLocaleBasedExportSupported = false;
193+
} else {
194+
// Log actual errors during normal fetch (not locale check)
195+
handleAndLogError(error, {
196+
...this.exportConfig.context,
197+
...(localeCode && { locale: localeCode }),
198+
});
185199
}
200+
186201
// Break to avoid infinite retry loop on errors
187202
break;
188203
}
@@ -312,4 +327,15 @@ export default class ExportTaxonomies extends BaseClass {
312327

313328
return localesToExport;
314329
}
330+
331+
private isLocalePlanLimitationError(error: any): boolean {
332+
return (
333+
error?.status === 403 &&
334+
error?.errors?.taxonomies?.some(
335+
(msg: string) =>
336+
msg.toLowerCase().includes('taxonomy localization') &&
337+
msg.toLowerCase().includes('not included in your plan'),
338+
)
339+
);
340+
}
315341
}

packages/contentstack-export/test/unit/export/modules/taxonomies.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,14 @@ describe('ExportTaxonomies', () => {
535535
});
536536

537537
it('should disable locale-based export on API error when checkLocaleSupport is true', async () => {
538+
// Create a structured API error (not a plan limitation error)
539+
const apiError: any = new Error('API Error');
540+
apiError.status = 500;
541+
apiError.errors = { general: ['Internal server error'] };
542+
538543
mockStackClient.taxonomy.returns({
539544
query: sinon.stub().returns({
540-
find: sinon.stub().rejects(new Error('API Error'))
545+
find: sinon.stub().rejects(apiError)
541546
})
542547
});
543548

@@ -546,6 +551,27 @@ describe('ExportTaxonomies', () => {
546551
// Should disable locale-based export on error
547552
expect(exportTaxonomies.isLocaleBasedExportSupported).to.be.false;
548553
});
554+
555+
it('should handle taxonomy localization plan limitation error gracefully', async () => {
556+
// Create the exact 403 error from the plan limitation
557+
const planLimitationError: any = new Error('Forbidden');
558+
planLimitationError.status = 403;
559+
planLimitationError.statusText = 'Forbidden';
560+
planLimitationError.errors = {
561+
taxonomies: ['Taxonomy localization is not included in your plan. Please contact the support@contentstack.com team for assistance.']
562+
};
563+
564+
mockStackClient.taxonomy.returns({
565+
query: sinon.stub().returns({
566+
find: sinon.stub().rejects(planLimitationError)
567+
})
568+
});
569+
570+
await exportTaxonomies.fetchTaxonomies('en-us', true);
571+
572+
// Should disable locale-based export and not throw error
573+
expect(exportTaxonomies.isLocaleBasedExportSupported).to.be.false;
574+
});
549575
});
550576

551577
describe('exportTaxonomies() method - locale-based export', () => {
@@ -586,6 +612,37 @@ describe('ExportTaxonomies', () => {
586612
mockGetLocales.restore();
587613
});
588614

615+
it('should clear taxonomies and re-fetch when falling back to legacy export', async () => {
616+
let fetchCallCount = 0;
617+
const mockFetchTaxonomies = sinon.stub(exportTaxonomies, 'fetchTaxonomies').callsFake(async (locale, checkSupport) => {
618+
fetchCallCount++;
619+
if (checkSupport) {
620+
// First call fails locale check
621+
exportTaxonomies.isLocaleBasedExportSupported = false;
622+
exportTaxonomies.taxonomies = { 'partial-data': { uid: 'partial-data' } }; // Simulate partial data
623+
} else {
624+
// Second call should have cleared data
625+
expect(exportTaxonomies.taxonomies).to.deep.equal({});
626+
}
627+
});
628+
const mockExportTaxonomies = sinon.stub(exportTaxonomies, 'exportTaxonomies').resolves();
629+
const mockWriteMetadata = sinon.stub(exportTaxonomies, 'writeTaxonomiesMetadata').resolves();
630+
const mockGetLocales = sinon.stub(exportTaxonomies, 'getLocalesToExport').returns(['en-us']);
631+
632+
await exportTaxonomies.start();
633+
634+
// Should call fetchTaxonomies twice: once for check, once for legacy
635+
expect(fetchCallCount).to.equal(2);
636+
// First call with locale, second without
637+
expect(mockFetchTaxonomies.firstCall.args).to.deep.equal(['en-us', true]);
638+
expect(mockFetchTaxonomies.secondCall.args).to.deep.equal([]);
639+
640+
mockFetchTaxonomies.restore();
641+
mockExportTaxonomies.restore();
642+
mockWriteMetadata.restore();
643+
mockGetLocales.restore();
644+
});
645+
589646
it('should use locale-based export when supported', async () => {
590647
const mockFetchTaxonomies = sinon.stub(exportTaxonomies, 'fetchTaxonomies').callsFake(async (locale, checkSupport) => {
591648
if (checkSupport) {

packages/contentstack-export/test/unit/utils/logger.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ describe('Logger', () => {
193193
});
194194

195195
it('should handle very long messages', async () => {
196-
const longMessage = 'A'.repeat(10000);
196+
const longMessage = 'A'.repeat(10);
197197

198198
// Should complete without throwing
199199
await loggerModule.log(mockExportConfig, longMessage, 'info');

packages/contentstack/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"@contentstack/cli-cm-branches": "~1.6.0",
2929
"@contentstack/cli-cm-bulk-publish": "~1.10.2",
3030
"@contentstack/cli-cm-clone": "~1.17.0",
31-
"@contentstack/cli-cm-export": "~1.21.0",
31+
"@contentstack/cli-cm-export": "~1.21.1",
3232
"@contentstack/cli-cm-export-to-csv": "~1.10.0",
3333
"@contentstack/cli-cm-import": "~1.29.0",
3434
"@contentstack/cli-cm-import-setup": "~1.7.0",

pnpm-lock.yaml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)