Skip to content

Commit 0fb8f11

Browse files
authored
Merge pull request #522 from contentstack/feature/locale-mapper-fix
Feature/locale mapper fix
2 parents d51eb03 + 1c7263f commit 0fb8f11

File tree

8 files changed

+74
-58
lines changed

8 files changed

+74
-58
lines changed

api/src/services/contentful.service.ts

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { JSDOM } from "jsdom";
1010
import { jsonToHtml, jsonToMarkdown, htmlToJson } from '@contentstack/json-rte-serializer';
1111

1212

13-
import { CHUNK_SIZE, MIGRATION_DATA_CONFIG, LOCALE_MAPPER } from "../constants/index.js";
13+
import { CHUNK_SIZE, LOCALE_MAPPER, MIGRATION_DATA_CONFIG } from "../constants/index.js";
1414
import { Locale } from "../models/types.js";
1515
import jsonRTE from "./contentful/jsonRTE.js";
1616
import { getAllLocales, getLogMessage } from "../utils/index.js";
@@ -86,6 +86,18 @@ function makeChunks(assetData: any) {
8686
return chunks;
8787
}
8888

89+
const mapLocales = ({ masterLocale, locale, locales }: any) => {
90+
if (locales?.masterLocale?.[masterLocale ?? ''] === locale) {
91+
return Object?.keys(locales?.masterLocale)?.[0]
92+
}
93+
for (const [key, value] of Object?.entries?.(locales) ?? {}) {
94+
if (typeof value !== 'object' && value === locale) {
95+
return key;
96+
}
97+
}
98+
return locale.toLowerCase();
99+
}
100+
89101
const transformCloudinaryObject = (input: any) => {
90102
const result: any = [];
91103
if (!Array.isArray(input)) {
@@ -122,7 +134,7 @@ const transformCloudinaryObject = (input: any) => {
122134
id: uuidv4(),
123135
folder: "",
124136
cs_metadata: {
125-
config_label: "config"
137+
config_label: "default_multi_config_key"
126138
}
127139
});
128140
}
@@ -702,17 +714,6 @@ const createEnvironment = async (packagePath: any, destination_stack_id: string,
702714
}
703715
};
704716

705-
const mapLocales = ({ masterLocale, locale, locales }: any) => {
706-
if (locales?.masterLocale?.[masterLocale ?? ''] === locale) {
707-
return Object?.keys(locales?.masterLocale)?.[0]
708-
}
709-
for (const [key, value] of Object?.entries?.(locales) ?? {}) {
710-
if (typeof value !== 'object' && value === locale) {
711-
return key;
712-
}
713-
}
714-
return locale.toLowerCase();
715-
}
716717

717718
/**
718719
* Creates and processes entries from a given package file and saves them to the destination stack directory.
@@ -740,7 +741,7 @@ const mapLocales = ({ masterLocale, locale, locales }: any) => {
740741
*
741742
* @throws Will log errors encountered during file reading, processing, or writing of entries.
742743
*/
743-
const createEntry = async (packagePath: any, destination_stack_id: string, projectId: string, contentTypes: any, mapperKeys: any, master_locale: string): Promise<void> => {
744+
const createEntry = async (packagePath: any, destination_stack_id: string, projectId: string, contentTypes: any, mapperKeys: any, master_locale: string, project: any): Promise<void> => {
744745
const srcFunc = 'createEntry';
745746
try {
746747
const entriesSave = path.join(DATA, destination_stack_id, ENTRIES_DIR_NAME);
@@ -749,7 +750,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
749750
const data = await fs.promises.readFile(packagePath, "utf8");
750751
const entries = JSON.parse(data)?.entries;
751752
const content = JSON.parse(data)?.contentTypes;
752-
753+
const LocaleMapper = { masterLocale: project?.master_locale ?? LOCALE_MAPPER?.masterLocale, ...project?.locales ?? {} };
753754
if (entries && entries.length > 0) {
754755
const assetId = await readFile(assetsSave, ASSETS_SCHEMA_FILE) ?? [];
755756
const entryId = await readFile(path.join(DATA, destination_stack_id, REFERENCES_DIR_NAME), REFERENCES_FILE_NAME);
@@ -799,13 +800,13 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
799800
});
800801
const pathName = getDisplayName(name, displayField);
801802
locales.forEach((locale) => {
802-
const localeCode = mapLocales({ masterLocale: master_locale, locale, locales: LOCALE_MAPPER });
803+
const localeCode = mapLocales({ masterLocale: master_locale, locale, locales: LocaleMapper });
803804
const publishDetails = Object?.values?.(environmentsId)?.length ? Object?.values?.(environmentsId)
804805
.filter((env: any) => env?.name === environment_id)
805806
?.map((env: any) => ({
806807
environment: env?.uid,
807808
version: 1,
808-
locale: locale?.toLowerCase?.(),
809+
locale: localeCode,
809810
})) : [];
810811
const title = fields?.[pathName]?.[locale] || "";
811812
const urlTitle = title
@@ -816,7 +817,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
816817
title: title?.trim?.() === "" ? (urlTitle || id) : title,
817818
uid: id,
818819
url: `/${name?.toLowerCase?.()}/${urlTitle}`,
819-
locale: locale?.toLowerCase?.(),
820+
locale: localeCode,
820821
publish_details: publishDetails,
821822
};
822823
// Format object keys to snake_case
@@ -843,11 +844,12 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
843844
for await (const [localeKey, localeValues] of Object.entries(
844845
values as { [key: string]: any }
845846
)) {
847+
const localeCode = mapLocales({ masterLocale: master_locale, locale: localeKey, locales: LocaleMapper });
846848
const chunks = makeChunks(localeValues);
847849
for (const [entryKey, entryValue] of Object.entries(localeValues)) {
848850
const message = getLogMessage(
849851
srcFunc,
850-
`Entry title "${(entryValue as { title: string })?.title}"(${ctName}) in the ${localeKey} locale has been successfully transformed.`,
852+
`Entry title "${(entryValue as { title: string })?.title}"(${ctName}) in the ${localeCode} locale has been successfully transformed.`,
851853
{}
852854
);
853855
await customLogger(projectId, destination_stack_id, "info", message);
@@ -856,7 +858,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
856858
let chunkIndex = 1;
857859
const filePath = path.join(
858860
entriesSave,
859-
ctName, localeKey.toLowerCase()
861+
ctName, localeCode
860862
);
861863
for await (const [chunkId, chunkData] of Object.entries(chunks)) {
862864
refs[chunkIndex++] = `${chunkId}-entries.json`;
@@ -874,7 +876,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
874876
await customLogger(projectId, destination_stack_id, 'info', message);
875877
}
876878
} catch (err) {
877-
console.info("🚀 ~ createEntry ~ err:", err)
879+
console.error("🚀 ~ createEntry ~ err:", err)
878880
const message = getLogMessage(
879881
srcFunc,
880882
`Error encountered while creating entries.`,
@@ -885,6 +887,10 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
885887
}
886888
};
887889

890+
function getKeyByValue(obj: Record<string, string>, targetValue: string): string | undefined {
891+
return Object.entries(obj).find(([_, value]) => value === targetValue)?.[0];
892+
}
893+
888894
/**
889895
* Processes and creates locale configurations from a given package file and saves them to the destination stack directory.
890896
*
@@ -909,7 +915,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
909915
*
910916
* @throws Will log errors encountered during file reading, processing, or writing of locale configurations.
911917
*/
912-
const createLocale = async (packagePath: string, destination_stack_id: string, projectId: string) => {
918+
const createLocale = async (packagePath: string, destination_stack_id: string, projectId: string, project: any) => {
913919
const srcFunc = 'createLocale';
914920
const localeSave = path.join(DATA, destination_stack_id, LOCALE_DIR_NAME);
915921
const globalFieldSave = path.join(DATA, destination_stack_id, GLOBAL_FIELDS_DIR_NAME);
@@ -933,30 +939,30 @@ const createLocale = async (packagePath: string, destination_stack_id: string, p
933939
)
934940
await customLogger(projectId, destination_stack_id, 'error', message);
935941
}
936-
937-
await Promise.all(locales.map(async (localeData: any) => {
938-
const title = localeData.sys.id;
942+
const fallbackMapLocales: any = { ...project?.master_locale ?? {}, ...project?.locales ?? {} }
943+
await Promise?.all(locales?.map?.(async (localeData: any) => {
944+
const currentMapLocale = getKeyByValue?.(fallbackMapLocales, localeData?.code) ?? `${localeData.code.toLowerCase()}`;
945+
const title = localeData?.sys?.id;
939946
const newLocale: Locale = {
940-
code: `${localeData.code.toLowerCase()}`,
941-
name: localeCodes?.[localeData.code.toLowerCase()] || "English - United States",
942-
fallback_locale: "",
947+
code: currentMapLocale,
948+
name: localeCodes?.[currentMapLocale] || "English - United States",
949+
fallback_locale: getKeyByValue(fallbackMapLocales, localeData?.fallbackCode) ?? '',
943950
uid: `${title}`,
944951
};
945952

946953
if (localeData.default === true) {
947954
msLocale[title] = newLocale;
948955
const message = getLogMessage(
949956
srcFunc,
950-
`Master Locale ${newLocale.code} has been successfully transformed.`,
957+
`Master Locale ${newLocale?.code} has been successfully transformed.`,
951958
{}
952959
)
953960
await customLogger(projectId, destination_stack_id, 'info', message);
954961
} else {
955-
newLocale.name = `${localeData.name}`;
956962
allLocales[title] = newLocale;
957963
const message = getLogMessage(
958964
srcFunc,
959-
`Locale ${newLocale.code} has been successfully transformed.`,
965+
`Locale ${newLocale?.code} has been successfully transformed.`,
960966
{}
961967
)
962968
await customLogger(projectId, destination_stack_id, 'info', message);

api/src/services/migration.service.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,12 @@ const startTestMigration = async (req: Request): Promise<any> => {
254254
break;
255255
}
256256
case CMS.CONTENTFUL: {
257-
await contentfulService?.createLocale(file_path, project?.current_test_stack_id, projectId);
257+
await contentfulService?.createLocale(file_path, project?.current_test_stack_id, projectId, project);
258258
await contentfulService?.createRefrence(file_path, project?.current_test_stack_id, projectId);
259259
await contentfulService?.createWebhooks(file_path, project?.current_test_stack_id, projectId);
260260
await contentfulService?.createEnvironment(file_path, project?.current_test_stack_id, projectId);
261261
await contentfulService?.createAssets(file_path, project?.current_test_stack_id, projectId, true);
262-
await contentfulService?.createEntry(file_path, project?.current_test_stack_id, projectId, contentTypes, project?.mapperKeys, project?.stackDetails?.master_locale);
262+
await contentfulService?.createEntry(file_path, project?.current_test_stack_id, projectId, contentTypes, project?.mapperKeys, project?.stackDetails?.master_locale, project);
263263
await contentfulService?.createVersionFile(project?.current_test_stack_id, projectId);
264264
break;
265265
}
@@ -326,18 +326,16 @@ const startMigration = async (req: Request): Promise<any> => {
326326
await wordpressService?.extractPosts(packagePath, project?.destination_stack_id, projectId, contentTypes, project?.mapperKeys, project?.stackDetails?.master_locale)
327327
await wordpressService?.extractGlobalFields(project?.destination_stack_id, projectId)
328328
await wordpressService?.createVersionFile(project?.destination_stack_id, projectId);
329-
330-
331329
}
332330
break;
333331
}
334332
case CMS.CONTENTFUL: {
335-
await contentfulService?.createLocale(file_path, project?.destination_stack_id, projectId);
333+
await contentfulService?.createLocale(file_path, project?.destination_stack_id, projectId, project);
336334
await contentfulService?.createRefrence(file_path, project?.destination_stack_id, projectId);
337335
await contentfulService?.createWebhooks(file_path, project?.destination_stack_id, projectId);
338336
await contentfulService?.createEnvironment(file_path, project?.destination_stack_id, projectId);
339337
await contentfulService?.createAssets(file_path, project?.destination_stack_id, projectId);
340-
await contentfulService?.createEntry(file_path, project?.destination_stack_id, projectId, contentTypes, project?.mapperKeys, project?.stackDetails?.master_locale);
338+
await contentfulService?.createEntry(file_path, project?.destination_stack_id, projectId, contentTypes, project?.mapperKeys, project?.stackDetails?.master_locale, project);
341339
await contentfulService?.createVersionFile(project?.destination_stack_id, projectId);
342340
break;
343341
}
@@ -478,8 +476,8 @@ export const updateLocaleMapper = async (req: Request) => {
478476
console.error(`project.json not found at ${projectFilePath}`);
479477
throw new Error(`project.json not found.`);
480478
}
481-
482479
// Find the project with the specified projectId
480+
await ProjectModelLowdb.read();
483481
const project: any = ProjectModelLowdb?.chain?.get?.("projects")?.find?.({ id: projectId })?.value();
484482
if (project) {
485483
const index = ProjectModelLowdb?.chain?.get("projects")?.findIndex?.({ id: projectId })?.value();

upload-api/migration-contentful/libs/contentTypeMapper.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,18 @@ const createFieldObject = (item, contentstackFieldType, backupFieldType, referen
129129
*/
130130
const createDropdownOrRadioFieldObject = (item, fieldType) => {
131131
let choices = [];
132-
if (!item?.validations?.length) {
133-
choices.push({ value: 'value', key: 'key' });
132+
if (item?.items?.validations?.length) {
133+
item?.items?.validations?.forEach?.((valid) => {
134+
valid.in?.forEach((value) => choices.push({ value: ["Symbol", "Text", "Array"].includes(item?.items?.type) ? `${value}` : value, key: `${value}` }));
135+
})
134136
} else {
135-
item.validations.forEach((valid) => {
136-
valid.in?.forEach((value) => choices.push({ value: ["Symbol", "Text", "Array"].includes(item.type) ? `${value}` : value, key: `${value}` }));
137-
});
137+
if (!item?.validations?.length) {
138+
choices.push({ value: 'value', key: 'key' });
139+
} else {
140+
item.validations.forEach((valid) => {
141+
valid.in?.forEach((value) => choices.push({ value: ["Symbol", "Text", "Array"].includes(item.type) ? `${value}` : value, key: `${value}` }));
142+
});
143+
}
138144
}
139145
return {
140146
...createFieldObject(item, fieldType, fieldType),
@@ -336,6 +342,7 @@ const contentTypeMapper = (data) => {
336342
break;
337343
}
338344
}
345+
339346
break;
340347
case 'Boolean':
341348
acc.push(createFieldObject(item, 'boolean', 'boolean'));

upload-api/migration-contentful/libs/extractLocale.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const extractLocale = async (jsonFilePath) => {
2121
if (Array?.isArray?.(jsonData?.locales)) {
2222
jsonData?.locales?.forEach?.(locale => {
2323
if (locale?.code) {
24-
uniqueLanguages.add(locale?.code?.toLowerCase?.()); // Normalize to lowercase
24+
uniqueLanguages.add(locale?.code); // Normalize to lowercase
2525
}
2626
});
2727
}

upload-api/migration-sitecore/libs/convert.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const read = require("fs-readdir-recursive");
1212
const helper = require("../utils/helper");
1313
const { MIGRATION_DATA_CONFIG } = require("../constants/index");
1414
const config = {
15-
"data": "./"+MIGRATION_DATA_CONFIG.DATA,
15+
"data": "./" + MIGRATION_DATA_CONFIG.DATA,
1616
"backup": "./backupMigrationData",
1717
"xml_filename": "",
1818
"sitecore_folder": "",
@@ -30,12 +30,21 @@ function ExtractFiles(sitecore_folder) {
3030
for (let i = 0; i < xml_folder.length; i++) {
3131
if (xml_folder?.[i]?.endsWith?.("xml")) {
3232
const xml_data = path?.join?.(sitecore_folder, xml_folder?.[i]);
33-
const json_data = xml_data.replace('xml', '');
34-
parseString(helper.readXMLFile(xml_data), { explicitArray: false }, function (err, result) {
33+
const jsonFilePath = xml_data?.replace?.('xml', '');
34+
parseString(helper?.readXMLFile?.(xml_data), { explicitArray: false }, function (err, result) {
3535
if (err) {
3636
console.error("failed to parse xml: ", err);
3737
} else {
38-
const filePath = path.join(json_data, config?.json_filename);
38+
const filePath = path.join(jsonFilePath, config?.json_filename);
39+
try {
40+
const jsonFileArray = read?.(jsonFilePath)?.filter?.((fileExt) => fileExt?.includes?.('.json')) ?? [];
41+
for (const ext of jsonFileArray) {
42+
const absolutePath = path?.resolve?.(path?.join?.(jsonFilePath, ext));
43+
fs?.unlinkSync?.(absolutePath);
44+
}
45+
} catch (error) {
46+
console.error("Error deleting file:", error);
47+
}
3948
fs.writeFileSync(filePath, JSON.stringify(result, null, 4), "utf8");
4049
}
4150
})

upload-api/migration-sitecore/libs/extractLocales.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const extractLocales = (dir) => {
1313

1414
if (item.isDirectory()) {
1515
extractLocales?.(fullPath); // Proper recursion
16-
} else if (item?.isFile() && item?.name === "data.json.json") {
16+
} else if (item?.isFile() && item?.name === "data.json") {
1717
try {
1818
const rawData = fs?.readFileSync?.(fullPath, "utf8");
1919
const jsonData = JSON?.parse?.(rawData);

upload-api/src/controllers/sitecore/index.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,8 @@ const {
1717
const createSitecoreMapper = async (filePath: string = "", projectId: string | string[], app_token: string | string[], affix: string | string[], config: object) => {
1818
try {
1919
const newPath = path.join(filePath, 'items');
20-
21-
const localeData = await extractLocales(path.join(filePath, 'items','master','sitecore','content'));
22-
console.log("Fetched Locales: ", localeData);
23-
24-
2520
await ExtractFiles(newPath);
21+
const localeData = await extractLocales(path.join(filePath, 'items', 'master', 'sitecore', 'content'));
2622
await ExtractConfiguration(newPath);
2723
await contentTypes(newPath, affix, config);
2824
const infoMap = await reference();
@@ -61,7 +57,7 @@ const createSitecoreMapper = async (filePath: string = "", projectId: string | s
6157
message: HTTP_TEXTS?.MAPPER_SAVED,
6258
});
6359
}
64-
60+
6561
const mapperConfig = {
6662
method: 'post',
6763
maxBodyLength: Infinity,
@@ -71,12 +67,12 @@ const createSitecoreMapper = async (filePath: string = "", projectId: string | s
7167
'Content-Type': 'application/json'
7268
},
7369
data: {
74-
locale:Array.from(localeData)
70+
locale: Array.from(localeData)
7571
},
7672
};
7773

7874
const mapRes = await axios.request(mapperConfig)
79-
if(mapRes?.status==200){
75+
if (mapRes?.status == 200) {
8076
logger.info('Legacy CMS', {
8177
status: HTTP_CODES?.OK,
8278
message: HTTP_TEXTS?.LOCALE_SAVED,

upload-api/src/validators/sitecore/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async function sitecoreValidator({ data }: props) {
3939
blob = await Promise.all(blob);
4040
mediaLibrary = await Promise.all(mediaLibrary);
4141

42-
if (templates?.length > 0 && content?.length > 0 && blob?.length > 0 && mediaLibrary?.length > 0) {
42+
if (templates?.length > 0 && content?.length > 0 && blob?.length > 0 && mediaLibrary?.length > 0) {
4343
return true;
4444
}
4545
return false;

0 commit comments

Comments
 (0)