diff --git a/packages/contentstack-export/src/export/modules/taxonomies.ts b/packages/contentstack-export/src/export/modules/taxonomies.ts index 5075a54fe..27be33d4c 100644 --- a/packages/contentstack-export/src/export/modules/taxonomies.ts +++ b/packages/contentstack-export/src/export/modules/taxonomies.ts @@ -22,6 +22,7 @@ export default class ExportTaxonomies extends BaseClass { private isLocaleBasedExportSupported: boolean = true; // Flag to track if locale-based export is supported private qs: { include_count: boolean; + include_publish_details: boolean; skip: number; asc?: string; limit: number; @@ -29,6 +30,7 @@ export default class ExportTaxonomies extends BaseClass { branch?: string; include_fallback?: boolean; fallback_locale?: string; + query?: Record; }; public taxonomiesFolderPath: string; private localesFilePath: string; @@ -38,7 +40,12 @@ export default class ExportTaxonomies extends BaseClass { this.taxonomies = {}; this.taxonomiesByLocale = {}; this.taxonomiesConfig = exportConfig.modules.taxonomies; - this.qs = { include_count: true, limit: this.taxonomiesConfig.limit || 100, skip: 0 }; + this.qs = { + include_count: true, + include_publish_details: true, + limit: this.taxonomiesConfig.limit || 100, + skip: 0, + }; this.applyQueryFilters(this.qs, 'taxonomies'); this.exportConfig.context.module = MODULE_CONTEXTS.TAXONOMIES; diff --git a/packages/contentstack-export/test/unit/export/modules/taxonomies.test.ts b/packages/contentstack-export/test/unit/export/modules/taxonomies.test.ts index 71e7dd8ba..276e0281e 100644 --- a/packages/contentstack-export/test/unit/export/modules/taxonomies.test.ts +++ b/packages/contentstack-export/test/unit/export/modules/taxonomies.test.ts @@ -66,7 +66,14 @@ describe('ExportTaxonomies', () => { taxonomies: { dirName: 'taxonomies', fileName: 'taxonomies.json', - invalidKeys: [], + invalidKeys: [ + 'updated_at', + 'created_by', + 'updated_by', + 'stackHeaders', + 'urlPath', + 'created_at', + ], limit: 100, }, locales: { @@ -107,6 +114,118 @@ describe('ExportTaxonomies', () => { sinon.restore(); }); + describe('include_publish_details (list query)', () => { + it('should set include_publish_details true on taxonomy list query params', () => { + const qs = (exportTaxonomies as any).qs; + expect(qs.include_publish_details).to.equal(true); + expect(qs.include_count).to.equal(true); + }); + + it('should pass include_publish_details true to taxonomy().query from fetchTaxonomies', async () => { + const querySpy = sinon.stub().returns({ + find: sinon.stub().resolves({ + items: [{ uid: 'taxonomy-1', name: 'Cat' }], + count: 1, + }), + }); + mockStackClient.taxonomy.returns({ query: querySpy }); + + await exportTaxonomies.fetchTaxonomies(); + + expect(querySpy.called).to.be.true; + expect(querySpy.firstCall.args[0]).to.have.property('include_publish_details', true); + }); + + it('should pass include_publish_details on every pagination request', async () => { + let callCount = 0; + const querySpy = sinon.stub().returns({ + find: sinon.stub().callsFake(() => { + callCount++; + if (callCount === 1) { + return Promise.resolve({ + items: Array(100).fill(null).map((_, i) => ({ uid: `taxonomy-${i}`, name: 'Test' })), + count: 150, + }); + } + return Promise.resolve({ + items: Array(50).fill(null).map((_, i) => ({ uid: `taxonomy-${100 + i}`, name: 'Test' })), + count: 150, + }); + }), + }); + mockStackClient.taxonomy.returns({ query: querySpy }); + + await exportTaxonomies.fetchTaxonomies(); + + expect(callCount).to.be.greaterThan(1); + querySpy.getCalls().forEach((call) => { + expect(call.args[0]).to.have.property('include_publish_details', true); + }); + }); + + it('should pass include_publish_details when fetching with locale code', async () => { + const querySpy = sinon.stub().returns({ + find: sinon.stub().resolves({ + items: [{ uid: 'taxonomy-1', name: 'Cat', locale: 'en-us' }], + count: 1, + }), + }); + mockStackClient.taxonomy.returns({ query: querySpy }); + + await exportTaxonomies.fetchTaxonomies('fr-fr'); + + expect(querySpy.called).to.be.true; + expect(querySpy.firstCall.args[0]).to.include({ + include_publish_details: true, + locale: 'fr-fr', + }); + }); + + it('should include include_publish_details on initializeExport count query', async () => { + const querySpy = sinon.stub().returns({ + find: sinon.stub().resolves({ items: [{ uid: 'taxonomy-1', name: 'Cat' }], count: 1 }), + }); + mockStackClient.taxonomy.returns({ query: querySpy }); + + const stubDetermine = sinon.stub(exportTaxonomies as any, 'determineExportStrategy').resolves(); + const stubFetchAll = sinon.stub(exportTaxonomies as any, 'fetchAllTaxonomies').resolves(); + const stubExportAll = sinon.stub(exportTaxonomies as any, 'exportAllTaxonomies').resolves(1); + const stubWriteMeta = sinon.stub(exportTaxonomies, 'writeTaxonomiesMetadata').resolves(); + const stubGetLocales = sinon.stub(exportTaxonomies, 'getLocalesToExport').returns(['en-us']); + + await exportTaxonomies.start(); + + expect(querySpy.called).to.be.true; + expect(querySpy.firstCall.args[0]).to.have.property('include_publish_details', true); + expect(querySpy.firstCall.args[0]).to.have.property('limit', 1); + + stubDetermine.restore(); + stubFetchAll.restore(); + stubExportAll.restore(); + stubWriteMeta.restore(); + stubGetLocales.restore(); + }); + + it('should merge exportConfig query.modules.taxonomies without removing root include_publish_details', () => { + const cfg = { + ...mockExportConfig, + query: { + modules: { + taxonomies: { branch: 'main' }, + }, + }, + } as any; + const instance = new ExportTaxonomies({ + exportConfig: cfg, + stackAPIClient: mockStackClient, + moduleName: 'taxonomies', + }); + const qs = (instance as any).qs; + expect(qs.include_publish_details).to.equal(true); + expect(qs.query).to.deep.equal({ branch: 'main' }); + }); + }); + describe('Constructor', () => { it('should initialize with correct parameters', () => { expect(exportTaxonomies).to.be.instanceOf(ExportTaxonomies); @@ -323,6 +442,21 @@ describe('ExportTaxonomies', () => { expect(Object.keys(exportTaxonomies.taxonomies).length).to.equal(0); }); + it('should retain publish_details when not listed in invalidKeys', () => { + const publishDetails = [{ environment: 'bltEnv1', time: '2026-04-21T12:00:00.000Z', user: 'bltUser1' }]; + const taxonomies = [ + { + uid: 'taxonomy-pub', + name: 'Pub', + publish_details: publishDetails, + }, + ]; + + exportTaxonomies.sanitizeTaxonomiesAttribs(taxonomies); + + expect(exportTaxonomies.taxonomies['taxonomy-pub'].publish_details).to.deep.equal(publishDetails); + }); + // const taxonomies = [ // { uid: 'taxonomy-1', name: 'Category' }, // { uid: 'taxonomy-2', name: 'Tag' }