Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8e750a8
Implement extractEntries functionality for WordPress migration; updat…
Apr 1, 2026
7425777
Refactor extractEntries logic to improve null safety and consistency;…
Apr 6, 2026
4dceab4
Enhance null safety in extractEntries by adding optional chaining to …
Apr 6, 2026
988e815
Update saveEntry function to enhance field UID matching logic for imp…
Apr 6, 2026
f4f58a9
Update lodash and jsdom versions in package.json and package-lock.jso…
Apr 6, 2026
9d207dc
Merge branch 'feature/delta-mig' into feature/cmg-842-delta-wordpress
Apr 6, 2026
d8cb5e5
fix(extractEntries): align source entry UID generation with WordPress…
Apr 7, 2026
fcb4c1a
updated package
Apr 8, 2026
f528919
refactor: enhance locale extraction process with async handling and i…
umeshmore45 Mar 27, 2026
5790738
refactor: optimize language extraction logic in extractLocales.js
umeshmore45 Mar 27, 2026
c51bb48
refactor: improve input validation and error handling in extractLocal…
umeshmore45 Mar 27, 2026
d5b61f9
refactor: enhance error handling and input validation in extractLocal…
umeshmore45 Mar 27, 2026
d7f2530
refactor: enhance input validation with optional chaining in extractL…
umeshmore45 Mar 27, 2026
575656a
refactor: enhance input validation with optional chaining in extractL…
umeshmore45 Mar 27, 2026
f1d538f
chore: remove unnecessary dev flags from package-lock.json entries
aishwarya-cstk Mar 30, 2026
a0dce81
chore: mark json-parse-even-better-errors and text-table as developme…
aishwarya-cstk Mar 30, 2026
6369d04
chore: remove development flags from json-parse-even-better-errors an…
aishwarya-cstk Mar 30, 2026
003be06
chore: mark json-parse-even-better-errors and text-table as developme…
aishwarya-cstk Mar 30, 2026
fc72715
chore: update yaml dependency version in package-lock.json from 1.10.…
aishwarya-cstk Mar 31, 2026
20cd293
chore: update package-lock.json to mark yaml as a development depende…
aishwarya-cstk Mar 31, 2026
416e7dc
chore: update lodash and lodash-es dependencies to version 4.18.1 acr…
aishwarya-cstk Apr 2, 2026
8a3ab58
refactor: update client import and variable naming in market-app.util…
aishwarya-cstk Apr 2, 2026
827e49f
refactor: simplify client import and update usage in market-app.utils…
aishwarya-cstk Apr 2, 2026
60eb3c2
feat: enhance entry field resolution and locale handling in Contentfu…
aishwarya-cstk Apr 6, 2026
3c96a24
fix: update taxonomy type to an empty string in ContentMapper component
aishwarya-cstk Apr 7, 2026
79abffd
refactor:resolved PR comments
aishwarya-cstk Apr 7, 2026
3db9743
chore: update dependencies in package-lock.json for api and ui, inclu…
aishwarya-cstk Apr 7, 2026
814c25d
Update lodash and jsdom versions in package.json and package-lock.jso…
Apr 6, 2026
b9b68b0
chore: diff dependency in package.json and package-lock.json
aishwarya-cstk Feb 12, 2026
a0a97b3
feat: enhance ContentMapper to support modular blocks and nested fiel…
aishwarya-cstk Feb 22, 2026
35d1fa9
feat: add Drupal support with MySQL configuration and update docker-c…
shobhitupadhyayy Mar 10, 2026
98f7912
fix: enhance null safety and add optional chaining across multiple se…
shobhitupadhyayy Mar 22, 2026
ef63c16
Update jsonpath dependency to version 1.2.1 in package.json and packa…
saurav-cstk Feb 13, 2026
226947a
updated entry-update script to handle entry based on cms, refactored …
yashin4112 Apr 6, 2026
d7e9756
updated package
Apr 8, 2026
8200837
Updated package-lock.json
Apr 8, 2026
bbaff2d
Updated packages and test coverages
Apr 9, 2026
540033d
Fix UI test coverage setup and GitHub Actions Rollup dependency issue
Apr 9, 2026
d99a0c7
Fix UI SWC native binding issue in GitHub Actions
Apr 9, 2026
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
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ jobs:
cache: npm
cache-dependency-path: ui/package-lock.json
- run: npm ci --legacy-peer-deps
- run: npm install @rollup/rollup-linux-x64-gnu --no-save
- run: npm run test:coverage
- uses: actions/upload-artifact@v4
if: always()
Expand Down
706 changes: 375 additions & 331 deletions api/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"html-to-json-parser": "^2.0.1",
"jsdom": "^24.1.0",
"jsonwebtoken": "^9.0.3",
"lodash": "^4.17.21",
"lodash": "^4.18.1",
"lowdb": "^7.0.1",
"mkdirp": "^3.0.1",
"mysql2": "^3.16.2",
Expand Down
114 changes: 97 additions & 17 deletions api/src/services/contentful.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,36 @@ const mapLocales = ({ masterLocale, locale, locales, isNull = false }: any) => {
}
}

function resolveEntryFieldKey(entry: Record<string, unknown>, baseKey: string): string | undefined {
if (baseKey in entry) return baseKey;
const snake = baseKey.replace(/([A-Z])/g, (m) => `_${m.toLowerCase()}`);
if (snake in entry) return snake;
return undefined;
}

/**
* Maps Contentful content type id → field id → whether that field is localized in the export schema.
* Used so we only fan out values for fields with `localized: false`, not for localized fields that
* happen to have a single locale in the entry (missing translations).
*/
function buildContentfulFieldLocalizedByContentType(
contentTypesFromPackage: any[]
): Map<string, Map<string, boolean>> {
const byCt = new Map<string, Map<string, boolean>>();
for (const ct of contentTypesFromPackage ?? []) {
const ctId = ct?.sys?.id;
if (!ctId) continue;
const byField = new Map<string, boolean>();
for (const f of ct?.fields ?? []) {
if (f?.id != null) {
byField.set(f.id, f.localized === true);
}
}
byCt.set(ctId, byField);
}
return byCt;
}

const transformCloudinaryObject = (input: any) => {
const result: any = [];
if (!Array.isArray(input)) {
Expand Down Expand Up @@ -777,6 +807,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
const data = await fs.promises.readFile(packagePath, "utf8");
const entries = JSON.parse(data)?.entries;
const content = JSON.parse(data)?.contentTypes;
const cfFieldLocalizedByCt = buildContentfulFieldLocalizedByContentType(content);
const LocaleMapper = { masterLocale: project?.master_locale ?? LOCALE_MAPPER?.masterLocale, ...project?.locales ?? {} };
if (entries && entries.length > 0) {
const assetId = await readFile(assetsSave, ASSETS_SCHEMA_FILE) ?? [];
Expand Down Expand Up @@ -814,7 +845,7 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
entryData[name][lang] ??= {};
entryData[name][lang][id] ??= {};
locales.push(lang);
const fieldData = currentCT?.fieldMapping?.find?.((item: any) => (key === item?.uid) && (!["text", "url"]?.includes?.(item?.backupFieldType)));
const fieldData = currentCT?.fieldMapping?.find?.((item: any) => key === item?.uid);
const newId = fieldData?.contentstackFieldUid ?? `${key}`?.replace?.(/[^a-zA-Z0-9]+/g, "_");
entryData[name][lang][id][newId] = processField(
langValue,
Expand Down Expand Up @@ -860,18 +891,73 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
);
});
});

// Non-localized Contentful fields (`localized: false` in the content type) only appear under
// one locale in exports. Copy them to every other locale branch so each slice is complete.
// Do not infer non-localized-ness from a single locale key — localized fields can legitimately
// have only one locale when translations are missing.
const entryLocaleKeys = new Set<string>();
for (const [, v] of Object?.entries?.(fields)) {
for (const lang of Object.keys(v as object)) {
entryLocaleKeys.add(lang);
}
}
const ct = contentTypes?.find((c: any) => c?.otherCmsUid === name);
for (const [key, value] of Object?.entries?.(fields)) {
const langs = Object?.keys(value as object);
if (langs?.length !== 1) continue;
const fd = ct?.fieldMapping?.find?.((item: any) => key === item?.uid);
const localizedInCf = cfFieldLocalizedByCt.get(name)?.get(key);
const explicitlyNonLocalized =
localizedInCf === false ||
(localizedInCf === undefined && fd?.advanced?.nonLocalizable === true);
if (!explicitlyNonLocalized) continue;
const srcLang = langs[0];
const newId = fd?.contentstackFieldUid ?? `${key}`?.replace?.(/[^a-zA-Z0-9]+/g, "_");
const srcEntry = entryData[name][srcLang]?.[id] as Record<string, unknown> | undefined;
if (!srcEntry) continue;
const fk = resolveEntryFieldKey(srcEntry, newId);
if (fk === undefined) continue;
for (const tgtLang of entryLocaleKeys) {
if (tgtLang === srcLang) continue;
entryData[name][tgtLang] ??= {};
entryData[name][tgtLang][id] ??= {};
const tgt = entryData[name][tgtLang][id] as Record<string, unknown>;
if (tgt[fk] === undefined) {
tgt[fk] = srcEntry[fk];
}
}
}

return entryData;
},
{}
);
for await (const [newKey, values] of Object.entries(result)) {
const currentCT = contentTypes?.find((ct: any) => ct?.otherCmsUid === newKey);
const ctName = currentCT?.contentstackUid in mapperKeys ?
mapperKeys?.[currentCT?.contentstackUid] : (currentCT?.contentstackUid ?? newKey.replace(/([A-Z])/g, "_$1").toLowerCase());
for await (const [localeKey, localeValues] of Object.entries(
values as { [key: string]: any }
)) {
const localeCode = mapLocales({ masterLocale: master_locale, locale: localeKey, locales: LocaleMapper, isNull: true });
mapperKeys?.[currentCT?.contentstackUid] : (currentCT?.contentstackUid ?? newKey?.replace?.(/([A-Z])/g, "_$1")?.toLowerCase?.());
const valuesByCfLocale = values as { [key: string]: { [uid: string]: Record<string, unknown> } };
const mergedByDestinationLocale: { [localeCode: string]: { [uid: string]: Record<string, unknown> } } = {};
for (const localeKey of Object.keys(valuesByCfLocale)) {
const localeValues = valuesByCfLocale[localeKey];
if (!localeValues) continue;
const localeCode = mapLocales({
masterLocale: master_locale,
locale: localeKey,
locales: LocaleMapper,
isNull: true,
});
if (!localeCode) continue;
mergedByDestinationLocale[localeCode] ??= {};
for (const [uid, entry] of Object.entries(localeValues)) {
mergedByDestinationLocale[localeCode][uid] = {
...(mergedByDestinationLocale[localeCode][uid] ?? {}),
...(entry ?? {}),
};
}
}
for await (const [localeCode, localeValues] of Object.entries(mergedByDestinationLocale)) {
const chunks = makeChunks(localeValues);
for (const [entryKey, entryValue] of Object.entries(localeValues)) {
const message = getLogMessage(
Expand All @@ -883,18 +969,12 @@ const createEntry = async (packagePath: any, destination_stack_id: string, proje
}
const refs: { [key: string]: any } = {};
let chunkIndex = 1;
if (localeCode) {
const filePath = path.join(
entriesSave,
ctName,
localeCode
);
for await (const [chunkId, chunkData] of Object.entries(chunks)) {
refs[chunkIndex++] = `${chunkId}-entries.json`;
await writeFile(filePath, `${chunkId}-entries.json`, chunkData);
}
await writeFile(filePath, ENTRIES_MASTER_FILE, refs);
const filePath = path.join(entriesSave, ctName, localeCode);
for await (const [chunkId, chunkData] of Object.entries(chunks)) {
refs[chunkIndex++] = `${chunkId}-entries.json`;
await writeFile(filePath, `${chunkId}-entries.json`, chunkData);
}
await writeFile(filePath, ENTRIES_MASTER_FILE, refs);
}
}
} else {
Expand Down
21 changes: 15 additions & 6 deletions api/src/services/wordpress.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,11 @@ async function saveEntry(fields: any, entry: any, file_path: string, assetData
const $ = cheerio.load(xmlData, { xmlMode: true });
const items = $('item');
const entryData: Record<string, any> = {};
const fieldList = Array.isArray(fields) ? fields : [];
const hasField = (uid: string) =>
fieldList.some(
(field: any) => field?.contentstackFieldUid === uid || field?.uid === uid
);

try {
if(entry ){
Expand Down Expand Up @@ -584,16 +589,20 @@ async function saveEntry(fields: any, entry: any, file_path: string, assetData
// Pass individual content to createSchema
entryData[uid] = await createSchema(fields, blocksJson, item?.title, uid, assetData, duplicateBlockMappings);
const categoryReference = extractCategoryReference(item?.['category']);
if (categoryReference?.length > 0) {
entryData[uid]['taxonomies'] = taxonomies;
if (hasField("taxonomies") && categoryReference?.length > 0) {
entryData[uid]["taxonomies"] = taxonomies;
}
const termsReference = extractTermsReference(item?.['category']);
if(termsReference?.length > 0) {
entryData[uid]['terms'] = terms;
if (hasField("terms") && termsReference?.length > 0) {
entryData[uid]["terms"] = terms;
}
entryData[uid]["tags"] = tags?.map((tag: any) => tag?.text) || [];
if (hasField("author")) {
entryData[uid]["author"] =
authorData?.filter((author: any) => author?.uid) || [];
}
entryData[uid]['tags'] = tags?.map((tag: any) => tag?.text);
entryData[uid]['author'] = authorData;
entryData[uid]['locale'] = locale;
entryData[uid]["publish_details"] = [];
entryData[uid]['publish_details'] = [];


Expand Down
10 changes: 5 additions & 5 deletions api/src/utils/market-app.utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import contentstack from '@contentstack/marketplace-sdk';
import {client} from '@contentstack/marketplace-sdk';
import { DEVURLS } from '../constants/index.js';




export const getAllApps = async ({ organizationUid, authtoken, region }: any) => {
try {
const client = contentstack.client({ authtoken, host: DEVURLS?.[region] ?? DEVURLS?.NA });
const data = await client.marketplace(organizationUid).findAllApps();
const contentstackclient = client({ authtoken, host: DEVURLS?.[region] ?? DEVURLS?.NA });
const data = await contentstackclient.marketplace(organizationUid).findAllApps();
return data?.items;
} catch (err) {
console.info("🚀 ~ getAllApps ~ err:", err)
Expand All @@ -16,8 +16,8 @@ export const getAllApps = async ({ organizationUid, authtoken, region }: any) =>

export const getAppManifestAndAppConfig = async ({ organizationUid, authtoken, region, manifestUid }: any) => {
try {
const client = contentstack.client({ authtoken, host: DEVURLS?.[region] ?? DEVURLS?.NA });
const data = await client.marketplace(organizationUid).app(manifestUid).fetch();
const contentstackclient = client({ authtoken, host: DEVURLS?.[region] ?? DEVURLS?.NA });
const data = await contentstackclient.marketplace(organizationUid).app(manifestUid).fetch();
return data;
} catch (err: any) {
console.info("🚀 ~ getAppManifestAndAppConfig ~ err:", err)
Expand Down
3 changes: 2 additions & 1 deletion api/src/validators/file-format.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ export default checkSchema({
errorMessage: VALIDATION_ERRORS.LENGTH_LIMIT.replace(/\$/g, "file_format"),
options: {
min: 1,
max: 400,
max: 200,
},
bail: true,
},
},
file_path: {
in: "body",
optional: { options: { nullable: true } },
isString: {
errorMessage: VALIDATION_ERRORS.STRING_REQUIRED.replace(/\$/g, "file_path"),
bail: true,
Expand Down
12 changes: 6 additions & 6 deletions api/sso.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,13 +367,13 @@ module.exports = async ({
};

if (ENCRYPT_KEY) {
if (appData.oauthData) {
appData.oauthData.client_id = encrypt(appData.oauthData.client_id);
appData.oauthData.client_secret = encrypt(appData.oauthData.client_secret);
if (appData?.oauthData) {
appData?.oauthData?.client_id = encrypt(appData?.oauthData?.client_id);
appData?.oauthData?.client_secret = encrypt(appData?.oauthData?.client_secret);
}
if (appData.pkce) {
appData.pkce.code_verifier = encrypt(appData.pkce.code_verifier);
appData.pkce.code_challenge = encrypt(appData.pkce.code_challenge);
if (appData?.pkce) {
appData?.pkce?.code_verifier = encrypt(appData?.pkce?.code_verifier);
appData?.pkce?.code_challenge = encrypt(appData?.pkce?.code_challenge);
}
} else {
console.warn("WARNING: MANIFEST_ENCRYPT_KEY not set — app.json will contain plaintext credentials");
Expand Down
Loading
Loading