Skip to content

Commit 8eb2965

Browse files
author
Your Name
committed
change master locale fix, change master locale script support for localised taxonomy, fixed taxonomy import showing success while all taxonomies were failed to import
1 parent 205b344 commit 8eb2965

2 files changed

Lines changed: 145 additions & 25 deletions

File tree

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

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,35 @@ export default class ImportTaxonomies extends BaseClass {
8484
log.debug('Using legacy folder structure for taxonomies', this.importConfig.context);
8585
}
8686

87-
//Step 5 create taxonomy & related terms success & failure file
87+
// Step 5: Flag taxonomies that were never processed (no matching export data
88+
// found in any locale/legacy path), so they don't silently disappear.
89+
for (const taxonomyUID of Object.keys(this.taxonomies || {})) {
90+
if (!(taxonomyUID in this.createdTaxonomies) && !(taxonomyUID in this.failedTaxonomies)) {
91+
log.error(
92+
`Taxonomy '${taxonomyUID}' could not be imported: no matching export data found`,
93+
this.importConfig.context,
94+
);
95+
this.failedTaxonomies[taxonomyUID] = this.taxonomies[taxonomyUID];
96+
}
97+
}
98+
99+
//Step 6 create taxonomy & related terms success & failure file
88100
log.debug('Creating success and failure files...', this.importConfig.context);
89101
this.createSuccessAndFailedFile();
90102

91-
log.success('Taxonomies imported successfully!', this.importConfig.context);
103+
const createdCount = Object.keys(this.createdTaxonomies).length;
104+
const failedCount = Object.keys(this.failedTaxonomies).length;
105+
106+
if (failedCount > 0) {
107+
log.error(
108+
`Taxonomies import completed with errors: ${createdCount} succeeded, ${failedCount} failed`,
109+
this.importConfig.context,
110+
);
111+
} else if (createdCount > 0) {
112+
log.success('Taxonomies imported successfully!', this.importConfig.context);
113+
} else {
114+
log.info('No taxonomies to import.', this.importConfig.context);
115+
}
92116
}
93117

94118
/**
@@ -367,13 +391,22 @@ export default class ImportTaxonomies extends BaseClass {
367391
const masterLocaleFolder = join(this.taxonomiesFolderPath, masterLocaleCode);
368392

369393
// Check if master locale folder exists (indicates new locale-based structure)
370-
if (!fileHelper.fileExistsSync(masterLocaleFolder)) {
371-
log.debug('No locale-based folder structure detected', this.importConfig.context);
372-
return false;
394+
if (fileHelper.fileExistsSync(masterLocaleFolder)) {
395+
log.debug('Locale-based folder structure detected', this.importConfig.context);
396+
return true;
373397
}
374398

375-
log.debug('Locale-based folder structure detected', this.importConfig.context);
399+
// The master locale may not have any localized taxonomies (so its folder was
400+
// never exported), but other locales can still use the locale-based structure.
401+
const locales = this.loadAvailableLocales();
402+
for (const localeCode of Object.keys(locales)) {
403+
if (fileHelper.fileExistsSync(join(this.taxonomiesFolderPath, localeCode))) {
404+
log.debug('Locale-based folder structure detected', this.importConfig.context);
405+
return true;
406+
}
407+
}
376408

377-
return true;
409+
log.debug('No locale-based folder structure detected', this.importConfig.context);
410+
return false;
378411
}
379412
}

packages/contentstack-migration/examples/change-master-locale/02-change-master-locale-new-file-structure.js

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ module.exports = async ({ migration, config }) => {
2525
}
2626

2727
async function tailorData() {
28-
let locales = await fs.readFile(pathValidator(path.resolve(sanitizePath(config.data_dir), 'locales/locales.json')), 'utf-8');
28+
let locales = await fs.readFile(
29+
pathValidator(path.resolve(sanitizePath(config.data_dir), 'locales/locales.json')),
30+
'utf-8',
31+
);
2932
let masterLocale = await fs.readFile(
3033
pathValidator(path.resolve(sanitizePath(config.data_dir), 'locales/master-locale.json')),
3134
'utf-8',
@@ -34,12 +37,14 @@ module.exports = async ({ migration, config }) => {
3437
if (masterLocale) {
3538
masterLocale = JSON.parse(masterLocale);
3639
masterLocale = Object.values(masterLocale);
37-
masterLocale = masterLocale[0]
38-
40+
masterLocale = masterLocale[0];
41+
3942
// Validate that we have a valid master locale code
40-
if (!masterLocale) {
43+
if (!masterLocale || !masterLocale.code) {
4144
throw new Error('Unable to determine master locale code from master-locale.json');
4245
}
46+
47+
masterLocale = masterLocale.code;
4348
}
4449
locales = JSON.parse(locales);
4550
let id = crypto.randomBytes(8).toString('hex');
@@ -60,6 +65,7 @@ module.exports = async ({ migration, config }) => {
6065
locales[id].fallback_locale = config.target_locale;
6166

6267
await handleEntries(masterLocale);
68+
await handleTaxonomies(masterLocale);
6369
await fs.writeFile(
6470
pathValidator(path.resolve(sanitizePath(config.data_dir), 'locales/locales.json')),
6571
JSON.stringify(locales),
@@ -84,21 +90,23 @@ module.exports = async ({ migration, config }) => {
8490
let sourceMasterLocaleEntries, targetMasterLocaleEntries;
8591

8692
// Check if index.json exists (if no entries, index.json won't be created)
87-
const indexFilePath = pathValidator(path.resolve(sanitizePath(config.data_dir), sanitizePath(`entries/${contentType}/${masterLocale}/index.json`)));
93+
const indexFilePath = pathValidator(
94+
path.resolve(
95+
sanitizePath(config.data_dir),
96+
sanitizePath(`entries/${contentType}/${masterLocale}/index.json`),
97+
),
98+
);
8899
if (!existsSync(indexFilePath)) {
89100
console.log(`Skipping ${contentType} - no index.json found (likely no entries)`);
90101
continue;
91102
}
92103

93-
sourceMasterLocaleEntries = await fs.readFile(
94-
indexFilePath,
95-
{ encoding: 'utf8' },
96-
);
104+
sourceMasterLocaleEntries = await fs.readFile(indexFilePath, { encoding: 'utf8' });
97105

98106
// Parse the index.json to get the entries file name
99107
const indexData = JSON.parse(sourceMasterLocaleEntries);
100108
const entriesFileName = Object.values(indexData)[0];
101-
109+
102110
// Check if we have a valid entries file name
103111
if (!entriesFileName) {
104112
console.log(`Skipping ${contentType} - no entries file found in index.json`);
@@ -112,10 +120,7 @@ module.exports = async ({ migration, config }) => {
112120
),
113121
);
114122

115-
sourceMasterLocaleEntries = await fs.readFile(
116-
entriesFilePath,
117-
{ encoding: 'utf8' },
118-
);
123+
sourceMasterLocaleEntries = await fs.readFile(entriesFilePath, { encoding: 'utf8' });
119124
sourceMasterLocaleEntries = JSON.parse(sourceMasterLocaleEntries);
120125
if (
121126
existsSync(pathValidator(path.resolve(config.data_dir, `entries/${contentType}/${config.target_locale}`)))
@@ -127,7 +132,7 @@ module.exports = async ({ migration, config }) => {
127132
if (targetMasterLocaleEntries) {
128133
const targetIndexData = JSON.parse(targetMasterLocaleEntries);
129134
const targetEntriesFileName = Object.values(targetIndexData)[0];
130-
135+
131136
if (targetEntriesFileName) {
132137
targetMasterLocaleEntries = await fs.readFile(
133138
pathValidator(
@@ -152,7 +157,7 @@ module.exports = async ({ migration, config }) => {
152157
Object.keys(sourceMasterLocaleEntries).forEach((uid) => {
153158
if (!targetMasterLocaleEntries[uid]) {
154159
targetMasterLocaleEntries[uid] = JSON.parse(JSON.stringify(sourceMasterLocaleEntries[uid]));
155-
delete targetMasterLocaleEntries[uid]['publish_details'];
160+
targetMasterLocaleEntries[uid]['publish_details'] = [];
156161
targetMasterLocaleEntries[uid].locale = config.target_locale;
157162
}
158163
});
@@ -164,10 +169,10 @@ module.exports = async ({ migration, config }) => {
164169
pathValidator(path.resolve(config.data_dir, `entries/${contentType}/${config.target_locale}/index.json`)),
165170
{ encoding: 'utf8', flag: 'a+' },
166171
);
167-
172+
168173
const existingIndexData = JSON.parse(exsitingTargetMasterLocalEntries);
169174
const existingEntriesFileName = Object.values(existingIndexData)[0];
170-
175+
171176
if (existingEntriesFileName) {
172177
await fs.writeFile(
173178
pathValidator(
@@ -194,6 +199,88 @@ module.exports = async ({ migration, config }) => {
194199
}
195200
}
196201

202+
async function handleTaxonomies(masterLocale) {
203+
const taxonomiesDirPath = pathValidator(path.resolve(sanitizePath(config.data_dir), 'taxonomies'));
204+
const taxonomiesIndexPath = pathValidator(path.resolve(taxonomiesDirPath, 'taxonomies.json'));
205+
206+
if (!existsSync(taxonomiesIndexPath)) {
207+
console.log('Skipping taxonomies - no taxonomies.json found');
208+
return;
209+
}
210+
211+
let taxonomiesIndex = await fs.readFile(taxonomiesIndexPath, { encoding: 'utf8' });
212+
taxonomiesIndex = JSON.parse(taxonomiesIndex);
213+
214+
const targetLocaleDirPath = pathValidator(path.resolve(taxonomiesDirPath, sanitizePath(config.target_locale)));
215+
216+
for (const taxonomyUid of Object.keys(taxonomiesIndex)) {
217+
const fileName = `${sanitizePath(taxonomyUid)}.json`;
218+
const targetFilePath = pathValidator(path.resolve(targetLocaleDirPath, fileName));
219+
220+
// Prefer the old master locale's taxonomy data, then the locale recorded at export time,
221+
// then fall back to any other locale that has it
222+
const exportedLocale = taxonomiesIndex[taxonomyUid]?.locale;
223+
let sourceFilePath;
224+
for (const localeCode of [masterLocale, exportedLocale]) {
225+
if (!localeCode) {
226+
continue;
227+
}
228+
const candidatePath = pathValidator(path.resolve(taxonomiesDirPath, sanitizePath(localeCode), fileName));
229+
if (existsSync(candidatePath)) {
230+
sourceFilePath = candidatePath;
231+
break;
232+
}
233+
}
234+
235+
if (!sourceFilePath) {
236+
const localeEntries = await fs.readdir(taxonomiesDirPath, { withFileTypes: true });
237+
for (const localeEntry of localeEntries) {
238+
if (!localeEntry.isDirectory() || localeEntry.name === config.target_locale) {
239+
continue;
240+
}
241+
const candidatePath = pathValidator(
242+
path.resolve(taxonomiesDirPath, sanitizePath(localeEntry.name), fileName),
243+
);
244+
if (existsSync(candidatePath)) {
245+
sourceFilePath = candidatePath;
246+
break;
247+
}
248+
}
249+
}
250+
251+
if (!sourceFilePath) {
252+
console.log(`Skipping taxonomy '${taxonomyUid}' - no source locale data found`);
253+
continue;
254+
}
255+
256+
let sourceTaxonomy = await fs.readFile(sourceFilePath, { encoding: 'utf8' });
257+
sourceTaxonomy = JSON.parse(sourceTaxonomy);
258+
259+
if (existsSync(targetFilePath)) {
260+
let targetTaxonomy = await fs.readFile(targetFilePath, { encoding: 'utf8' });
261+
targetTaxonomy = JSON.parse(targetTaxonomy);
262+
targetTaxonomy.terms = targetTaxonomy.terms || [];
263+
264+
const existingTermUids = new Set(targetTaxonomy.terms.map((term) => term.uid));
265+
for (const term of sourceTaxonomy.terms || []) {
266+
if (!existingTermUids.has(term.uid)) {
267+
targetTaxonomy.terms.push(JSON.parse(JSON.stringify(term)));
268+
}
269+
}
270+
271+
await fs.writeFile(targetFilePath, JSON.stringify(targetTaxonomy));
272+
} else {
273+
await fs.mkdir(targetLocaleDirPath, { recursive: true });
274+
275+
const targetTaxonomy = JSON.parse(JSON.stringify(sourceTaxonomy));
276+
targetTaxonomy.taxonomy = targetTaxonomy.taxonomy || {};
277+
targetTaxonomy.taxonomy.locale = config.target_locale;
278+
279+
await fs.writeFile(targetFilePath, JSON.stringify(targetTaxonomy));
280+
}
281+
}
282+
}
283+
197284
await tailorData();
198285
},
199286
};

0 commit comments

Comments
 (0)