FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(import): remap nested entry-data extension UIDs for is_asset custom fields [DX-7312] by harshitha-cstk · Pull Request #359 · contentstack/cli-plugins · GitHub

fix(import): remap nested entry-data extension UIDs for is_asset custom fields [DX-7312] - #359

Open
harshitha-cstk wants to merge 5 commits into
developmentfrom
fix/dx-7312-entry-data-extension-uid-remap
Open

fix(import): remap nested entry-data extension UIDs for is_asset custom fields [DX-7312]#359
harshitha-cstk wants to merge 5 commits into
developmentfrom
fix/dx-7312-entry-data-extension-uid-remap

Conversation

harshitha-cstk commented Aug 25, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Summary

Closes both halves of DX-7312 (marketplace-app extension UIDs not remapped on clone/import):

  1. Entries — silent data loss: nested is_asset custom-field values (e.g. image_presets) kept the source app's extension_uid, which the audit then stripped.
  2. Global Fields / Content Types — import error: extension_uid … does not exist in destination stack, because the marketplace UID mapping was never written.

Root Causes & Fixes

1. Entry-data remap — utils/asset-helper.ts

lookupAssets → findAssetIdsFromJsonCustomFields only walked top-level schema fields, so an is_asset JSON custom field nested inside a group / global_field / blocks never had its metadata.extension_uid remapped → orphaned reference → the audit deleted the field (silent data loss).

Fix: replaced the flat map with a recursive walk (remapJsonCustomFieldExtensionUids) that follows the entry-data shape through group / global_field / blocks, handles multiple-valued fields, and remaps via the marketplace_apps mapper.

2. Marketplace config decrypt resilience — import/modules/marketplace-apps.ts

updateAppsConfig decrypted inline as the call argument:

.setConfiguration(this.nodeCrypto.decrypt(configuration))

nodeCrypto.decrypt is synchronous, so a bad-decrypt (ERR_OSSL_BAD_DECRYPT — an app config encrypted with a different key) threw outside the promise chain's .catch, propagated through installApps (which re-throws), and aborted the entire marketplace-apps module → mapper/marketplace_apps/uid-mapping.json was never written → GF/CT/entry remap had nothing to map against.

Fix: decrypt inside a try/catch for both configuration and server_configuration. On failure, log a warn and skip only that app's config push. The app stays installed, its extension_uid mapping is still recorded, the module completes, and the mapping file is written.

Testing

Validated with the dev CLI (node bin/dev cm:stacks:import) into fresh stacks:

  • Entries: nested image_presets remapped blt8eb76d16603badf3 → destination IPB UID; field preserved; 391/391 items, 0 stripped.
  • Marketplace / GF / CT: re-ran the export that reproduced the ticket.
    • Before: module aborted, marketplace_apps/uid-mapping.json empty, page_banner/table failed with extension_uid … does not exist.
    • After: WARN: Failed to decrypt configuration for 'Image Preset Builder' … skipping; Install Apps 4/4; mapping written with 8 extension entries; Global Fields 36/36, 0 extension does not exist errors.

🤖 Generated with Claude Code

…om fields

Marketplace-app / extension references embedded in entry DATA (e.g. an
`image_presets` value's `metadata.extension_uid`) are stack-scoped and must
be remapped to the destination app's extension_uid during import. The remap
in `lookupAssets` (`findAssetIdsFromJsonCustomFields`) only walked top-level
schema fields, so any `is_asset` JSON custom field nested inside a
group / global_field / blocks was skipped.

Consequences of the miss:
- the entry kept the source app's extension_uid, leaving an orphaned
  reference on the destination stack; and
- on a subsequent import the audit flagged that UID as a missing reference
  and stripped the whole field (silent data loss of image-preset configs).

Replace the flat `ctSchema.map` with a recursive walk
(`remapJsonCustomFieldExtensionUids`) that follows the entry-data shape
through group / global_field / blocks and handles multiple-valued fields,
remapping `metadata.extension_uid` (and the schema field's extension_uid)
via the marketplace_apps mapping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
harshitha-cstk requested a review from a team as a code owner August 25, 2026 08:36

snyk-io Bot commented Aug 25, 2026
edited
Loading

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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 0 0 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

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 0 90 / 365 days ✅ Passed
🔵 Low 0 0 180 / 365 days ✅ Passed

✅ BUILD PASSED - All security checks passed

harshitha-cstk self-assigned this Aug 25, 2026
const values = Array.isArray(value) ? value : [value];
for (const val of values) {
const currentUid = val?.metadata?.extension_uid;
if (currentUid && installedExtensions && installedExtensions[currentUid]) {

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

can we use switch case here instead of multiple if...else

cs-raj previously approved these changes Aug 25, 2026

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.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull request overview

This PR remaps marketplace extension UIDs in nested JSON asset fields during imports.

Changes:

  • Recursively traverses groups, global fields, blocks, and arrays.
  • Remaps entry metadata and schema extension UIDs.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +193 to +197
} else if (data_type === 'group' || data_type === 'global_field') {
remapJsonCustomFieldExtensionUids(field.schema, value);
} else if (data_type === 'blocks' && Array.isArray(field.blocks)) {
const blockInstances = Array.isArray(value) ? value : [value];
for (const blockInstance of blockInstances) {
Comment on lines +193 to +194
} else if (data_type === 'group' || data_type === 'global_field') {
remapJsonCustomFieldExtensionUids(field.schema, value);
…borting the module

`updateAppsConfig` called `nodeCrypto.decrypt(configuration)` inline as the
argument to `.setConfiguration(...)` / `.setServerConfig(...)`. A bad-decrypt
(e.g. ERR_OSSL_BAD_DECRYPT when an app's exported `configuration` was
encrypted with a different key) therefore threw synchronously — outside the
promise chain's `.catch` — and propagated up through `installApps` (which
re-throws), aborting the entire marketplace-apps module.

Because the module aborted, `mapper/marketplace_apps/uid-mapping.json` was
never written. Global fields / content types (and entry data) then had no
marketplace extension mapping to remap against, so their imports failed with
"extension_uid ... does not exist in destination stack".

Fix: decrypt inside a try/catch for both configuration and server_configuration.
On failure, log a warning and skip only that app's config push; the app stays
installed and its extension_uid mapping is still recorded, so downstream
GF/CT/entry remapping works and the uid-mapping file is written.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 0 244 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

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 0 90 / 365 days ✅ Passed
🔵 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: 244
  • Medium without fixes: 0
  • Low without fixes: 0

✅ BUILD PASSED - All security checks passed

Address PR review on the entry-data remap:

- Replace the schema-driven walker with `remapEntryMetadataExtensionUids`, a
  schema-independent deep walk of the entry data that remaps any
  `metadata.extension_uid` via the marketplace_apps mapping. This is robust to
  is_asset custom fields nested in group / global_field / blocks AND to
  content-type schemas that carry a reference-only global_field stub (no
  expanded `schema`, e.g. from query-export), which the previous schema-driven
  walk would silently skip.
- Run it once, unconditionally, after `find()` (idempotent — already-mapped
  UIDs are no-ops).
- Guard `find()` against a missing schema so a global_field stub no longer
  throws while collecting assets.
- Keep the schema field `extension_uid` remap for parity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 0 244 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

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 0 90 / 365 days ✅ Passed
🔵 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: 244
  • Medium without fixes: 0
  • Low without fixes: 0

✅ BUILD PASSED - All security checks passed

Address PR review on the entry-data remap:

- Replace the schema-driven walker with `remapEntryMetadataExtensionUids`, a
  schema-independent deep walk of the entry data that remaps any
  `metadata.extension_uid` via the marketplace_apps mapping. This is robust to
  is_asset custom fields nested in group / global_field / blocks AND to
  content-type schemas that carry a reference-only global_field stub (no
  expanded `schema`, e.g. from query-export), which the previous schema-driven
  walk would silently skip.
- Run it once, unconditionally, after `find()` (idempotent — already-mapped
  UIDs are no-ops).
- Guard `find()` against a missing schema so a global_field stub no longer
  throws while collecting assets.
- Keep the schema field `extension_uid` remap for parity.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

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 0 244 25 ✅ Passed
🟡 Medium Severity 0 0 500 ✅ Passed
🔵 Low Severity 0 0 1000 ✅ Passed

⏱️ SLA Breach Summary

✅ No SLA breaches detected. All vulnerabilities are within acceptable time thresholds.

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 0 90 / 365 days ✅ Passed
🔵 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: 244
  • Medium without fixes: 0
  • Low without fixes: 0

✅ BUILD PASSED - All security checks passed

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants


Back | FazBrowse Home | New Git URL