CMG-998 | Locale Based Delta Migration#1093
Conversation
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ 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:
Consider reviewing these vulnerabilities when fixes become available. |
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ 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:
Consider reviewing these vulnerabilities when fixes become available. |
There was a problem hiding this comment.
⚠️ 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.
| 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); |
| 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; |
| const localePath = path.join(ctPath, localeName); | ||
| if (!isDir(localePath)) continue; | ||
| const sourceUids = collectSourceUidsFromLocaleDir(localePath); | ||
| if (!sourceUids.length) continue; |
| } | ||
| }); | ||
| setLocaleOptions(opts); | ||
| if (opts.length > 0) setSelectedLocale(opts[0]); |
| // 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) { |
e4a72d0 to
e61f5e5
Compare
e61f5e5 to
1e543ab
Compare
🔒 Security Scan Results
⏱️ SLA Breach Summary
ℹ️ 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:
Consider reviewing these vulnerabilities when fixes become available. |
🔗 Jira Ticket
📋 PR Type
📝 Description
What changed?
migrated_localeson the project and per-localeentryByLocalein the uid mapper so we know what exists in which locale.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 backendui— React frontendupload-api— Upload API serverdocker/docker-compose🧪 How to Test
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
🔗 Related PRs / Dependencies
✅ Author Checklist
feature/cmg-998-locale-based-delta.env/example.envupdated if new environment variables were added (n/a)npm test)README.md/ docs updated if behaviour changed👀 Reviewer Notes
migrated_locales,EntryMapper.language,UidMapper.entryByLocale) are optional with lazy backfill — no data migration needed.writeUidMappingis untouched; per-locale mapping lives in a newwritePerLocaleEntryUidMapping.