Skip to content

CMG-998 | Locale Based Delta Migration#1093

Open
chetan-contentstack wants to merge 4 commits into
devfrom
feature/cmg-998-locale-based-delta
Open

CMG-998 | Locale Based Delta Migration#1093
chetan-contentstack wants to merge 4 commits into
devfrom
feature/cmg-998-locale-based-delta

Conversation

@chetan-contentstack

@chetan-contentstack chetan-contentstack commented Jun 16, 2026

Copy link
Copy Markdown

🔗 Jira Ticket

[CMG-998] Delta Migration | Locale Based Delta Migration


📋 PR Type

  • ✨ Feature
  • 🐛 Bug Fix
  • 🔥 Hotfix
  • ♻️ Refactor
  • 🧹 Chore / Dependency Update
  • 📝 Documentation

📝 Description

What changed?

  • Delta decision is now per-locale instead of global — new locales added on restart run a full migration; existing locales run delta.
  • Map Entry step gets a locale dropdown; entry updates are toggled per locale and pushed to the matching locale variant in the destination stack.
  • Tracks migrated_locales on the project and per-locale entryByLocale in the uid mapper so we know what exists in which locale.
  • Wired WordPress extractEntries (was unused) and added master-locale entry fan-out so non-master variants actually land in the stack.

Why?

Delta previously treated all locales as one dimension, so newly-added locales weren't handled and non-master updates never landed. This makes delta locale-aware end-to-end.


🧩 Affected Areas

  • api — Node.js backend
  • ui — React frontend
  • upload-api — Upload API server
  • docker / docker-compose
  • CI / GitHub Actions workflows
  • Environment variables / config
  • Other:

🧪 How to Test

  1. Create a project with two locales mapped (master + one additional). Run a full migration; confirm entries land in both locales.
  2. Restart the project, go to Step 4 — Map Entry. Verify the locale dropdown appears top-right and defaults to master.
  3. Switch the dropdown to the additional locale, check some entries, save, run migration. Updates should land only in that locale.
  4. Add a new locale after an initial migration and restart. The new locale should run a full import; the existing one should run delta.
  5. WordPress: upload a WP XML on a multi-locale project. Confirm entry_mapper is populated and entries appear in both locale folders of the destination stack.

Expected result: Per-locale isolation across the delta flow — new locales get full migration, existing locales push only the entries selected for that locale.


📸 Screenshots / Recordings

Before After

🔗 Related PRs / Dependencies


✅ Author Checklist

  • Branch follows naming convention: feature/cmg-998-locale-based-delta
  • Jira ticket linked above
  • Self-reviewed the diff — no debug logs, commented-out code, or TODOs left in
  • .env / example.env updated if new environment variables were added (n/a)
  • No sensitive credentials or secrets committed
  • Existing tests pass locally (npm test)
  • New tests written (or not applicable — explain why)
  • README.md / docs updated if behaviour changed
  • Talisman pre-push scan passes (no secrets flagged)

👀 Reviewer Notes

  • Schema additions (migrated_locales, EntryMapper.language, UidMapper.entryByLocale) are optional with lazy backfill — no data migration needed.
  • writeUidMapping is untouched; per-locale mapping lives in a new writePerLocaleEntryUidMapping.
  • Test-stack flow isn't affected by this change; out of scope here.
  • Vanilla WordPress exports have no per-locale content, so the fan-out duplicates master content into each locale folder for variants to exist (real translations need WPML/Polylang).

Migration v2 · Docs · Issues

@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 11 338 25 ✅ Passed
🟡 Medium Severity 32 2 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

⚠️ Warning: The following vulnerabilities have exceeded their SLA thresholds (days since publication).

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 2 90 / 365 days ⚠️ Warning
🔵 Low 0 0 180 / 365 days ✅ Passed

ℹ️ Vulnerabilities Without Available Fixes (Informational Only)

The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:

  • Critical without fixes: 0
  • High without fixes: 338
  • Medium without fixes: 2
  • Low without fixes: 0

⚠️ BUILD PASSED WITH WARNINGS - SLA breaches detected for issues without available fixes

Consider reviewing these vulnerabilities when fixes become available.

@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 11 338 25 ✅ Passed
🟡 Medium Severity 33 2 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

⚠️ Warning: The following vulnerabilities have exceeded their SLA thresholds (days since publication).

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 2 90 / 365 days ⚠️ Warning
🔵 Low 0 0 180 / 365 days ✅ Passed

ℹ️ Vulnerabilities Without Available Fixes (Informational Only)

The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:

  • Critical without fixes: 0
  • High without fixes: 338
  • Medium without fixes: 2
  • Low without fixes: 0

⚠️ BUILD PASSED WITH WARNINGS - SLA breaches detected for issues without available fixes

Consider reviewing these vulnerabilities when fixes become available.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

The Step 4 Entry Mapper UI currently can leave stale entry-table data visible when content type search/filter returns no results, and there is also a user-facing copy regression.

Pull request overview

This PR makes delta migration locale-aware end-to-end, so newly-added locales run a full migration while previously migrated locales run delta, and Step 4 (Map Entry) can toggle entry updates per locale.

Changes:

  • Adds per-locale decisioning and persistence (migrated_locales, locale helpers) plus per-locale UID mapping (entryByLocale) to support locale-scoped delta behavior.
  • Updates Step 4 (Map Entry) UI/API to fetch and save entry update selections per locale via a locale dropdown.
  • Improves WordPress import pipeline to populate entry mappings (extractEntries) and fan-out master entries into additional locale folders so variants exist for each mapped locale.
File summaries
File Description
upload-api/tests/unit/controllers/wordpress.controller.test.ts Extends mocks for the newly-wired WordPress extractEntries call.
upload-api/src/controllers/wordpress/index.ts Calls extractEntries to enrich content types with entry mappings before posting mapper data.
upload-api/package.json Adds/adjusts npm overrides (e.g., diff, @wordpress/data).
upload-api/migration-wordpress/index.ts Exposes extractEntries from the local migration-wordpress package.
ui/src/services/api/migration.service.ts Adds optional locale query parameter support to getEntryMapping.
ui/src/components/ContentMapper/index.scss Styles the per-locale dropdown positioning in Step 4.
ui/src/components/ContentMapper/entryMapper.tsx Adds locale dropdown, locale-scoped fetch/save, and UI behavior adjustments for Step 4.
api/tests/unit/utils/uid-mapper.utils.test.ts Adds unit coverage for writePerLocaleEntryUidMapping.
api/tests/unit/utils/locale-migration.utils.test.ts Adds unit coverage for new locale migration helper utilities.
api/src/utils/uid-mapper.utils.ts Adds writePerLocaleEntryUidMapping to populate uidMapper.entryByLocale.
api/src/utils/locale-migration.utils.ts Introduces helpers for mapped locales, migrated locales, and per-locale full/delta decisions.
api/src/utils/entry-update.utils.ts Makes delta cleanup/update collection locale-aware and emits per-locale update payload metadata.
api/src/utils/entry-update-script.cjs Updates entry update script to apply updates to the correct locale variant.
api/src/services/wordpress.service.ts Fans out master-locale entries into additional locale folders for variant creation.
api/src/services/runCli.service.ts Writes per-locale UID mapping after CLI import and records migrated_locales.
api/src/services/contentMapper.service.ts Adds locale filtering for entry mapping retrieval (Step 4 API).
api/src/models/uidMapper.ts Adds entryByLocale to the UID mapper model defaults/types.
api/src/models/project-lowdb.ts Extends project model interface to include locale mapping and migration tracking fields.
api/src/models/EntryMapper.ts Extends EntryMapper type to include optional language (source locale) field.

Copilot's findings

  • Files reviewed: 19/19 changed files
  • Comments generated: 3

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 229 to +232
const { data } = await getContentTypes(projectId, 0, 1000, searchCT || '', 'old');
const nextContentTypes = data?.contentTypes ?? [];
setContentTypes(nextContentTypes);
setFilteredContentTypes(nextContentTypes);
setCount(nextContentTypes?.length ?? 0);
// No matching content types → clear the right panel so the previous CT's table doesn't stick around.
if (!nextContentTypes?.length) {
resetEntryTable();
}
setContentTypes(data?.contentTypes ?? []);
setFilteredContentTypes(data?.contentTypes ?? []);
setCount(data?.contentTypes?.length ?? 0);
Comment on lines 290 to 294
const filteredCT = contentTypes?.filter((ct) => CONTENT_MAPPING_STATUS[ct?.status] === value);
if (value !== 'All') {
setFilteredContentTypes(filteredCT);
setCount(filteredCT?.length);
// Filter yielded no content types → clear the right panel so a stale entry table doesn't linger.
if (!filteredCT?.length) {
resetEntryTable();
setShowFilter(false);
return;
}
const selectedIndex = filteredCT.findIndex((ct) => ct?.otherCmsUid === otherCmsUid);

const modalProps = {
body: 'An error occurred while generating the content mapper. Please go to the Legacy CMS step and validate the file again.',
body: 'There is something error occured while generating content mapper. Please go to Legacy Cms step and validate the file again.',
}
}

if (!Object.keys(entryByLocale).length) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?.

const localePath = path.join(ctPath, localeName);
if (!isDir(localePath)) continue;
const sourceUids = collectSourceUidsFromLocaleDir(localePath);
if (!sourceUids.length) continue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?.

}
});
setLocaleOptions(opts);
if (opts.length > 0) setSelectedLocale(opts[0]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?.

// Populate per-content-type `entryMapping` from the WXR items. Without this the backend
// content_mapper writes no entry_mapper rows, which leaves the delta-migration Step 4
// (Map Entry) empty on restart.
if (Array.isArray(contentTypeData) && contentTypeData.length > 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?.

@chetan-contentstack chetan-contentstack force-pushed the feature/cmg-998-locale-based-delta branch from e4a72d0 to e61f5e5 Compare June 16, 2026 13:02
@chetan-contentstack chetan-contentstack force-pushed the feature/cmg-998-locale-based-delta branch from e61f5e5 to 1e543ab Compare June 16, 2026 13:04
@github-actions

Copy link
Copy Markdown

🔒 Security Scan Results

ℹ️ Note: Only vulnerabilities with available fixes (upgrades or patches) are counted toward thresholds.

Check Type Count (with fixes) Without fixes Threshold Result
🔴 Critical Severity 0 0 10 ✅ Passed
🟠 High Severity 12 338 25 ✅ Passed
🟡 Medium Severity 35 2 500 ✅ Passed
🔵 Low Severity 1 0 1000 ✅ Passed

⏱️ SLA Breach Summary

⚠️ Warning: The following vulnerabilities have exceeded their SLA thresholds (days since publication).

Severity Breaches (with fixes) Breaches (no fixes) SLA Threshold (with/no fixes) Status
🔴 Critical 0 0 15 / 30 days ✅ Passed
🟠 High 0 0 30 / 120 days ✅ Passed
🟡 Medium 0 2 90 / 365 days ⚠️ Warning
🔵 Low 0 0 180 / 365 days ✅ Passed

ℹ️ Vulnerabilities Without Available Fixes (Informational Only)

The following vulnerabilities were detected but do not have fixes available (no upgrade or patch). These are excluded from failure thresholds:

  • Critical without fixes: 0
  • High without fixes: 338
  • Medium without fixes: 2
  • Low without fixes: 0

⚠️ BUILD PASSED WITH WARNINGS - SLA breaches detected for issues without available fixes

Consider reviewing these vulnerabilities when fixes become available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants