@salesforce/afv-skills 1.55.0 → 1.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/agentforce-generate/SKILL.md +8 -0
- package/skills/agentforce-generate/references/actions-reference.md +2 -2
- package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
- package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
- package/skills/agentforce-observe/SKILL.md +13 -9
- package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
- package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
- package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
- package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
- package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
- package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
- package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
- package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
- package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
- package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
- package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
- package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
- package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
- package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
- package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
- package/skills/dx-org-analyze/SKILL.md +0 -1
- package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
- package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
- package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
- package/skills/dx-org-manage/SKILL.md +6 -50
- package/skills/dx-org-manage/examples/README.md +10 -16
- package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
- package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
- package/skills/dx-org-shape-manage/SKILL.md +4 -3
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
- package/skills/experience-accessibility-validate/SKILL.md +8 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
- package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
- package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
- package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
- package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
- package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
- package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
- package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
- package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
- package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
- package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
- package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
- package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
- package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
- package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
- package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
- package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
- package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
- package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
- package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
- package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
- package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
- package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
- package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
- package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
- package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
- package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
- package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
- package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
- package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
- package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
- package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
- package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
- package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
- package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
- package/skills/mobile-apps-create/SKILL.md +1 -1
- package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
- package/skills/platform-sandbox-configure/SKILL.md +108 -133
- package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
- package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
- package/skills/platform-widget-generate/SKILL.md +26 -18
- package/skills/platform-widget-generate/examples/formulas.json +191 -0
- package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
- package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
- package/skills/sales-call-scoring-configure/SKILL.md +318 -0
- package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
- package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
- package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
- package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
- package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
- package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
- package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
- package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
- package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
- package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
- package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
- package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
- package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
- package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
- package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
- package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
- package/skills/service-de-channel-activate/SKILL.md +6 -5
- package/skills/service-de-channel-create/SKILL.md +3 -3
- package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
- package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
- package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
- package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
- package/skills/service-helpagent-coordinate/SKILL.md +3 -2
- package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
- package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
- package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
- package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
- package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
- package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
- package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
- package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
- package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
- package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
- package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
- package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
- package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
- package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
- package/skills/dx-org-manage/references/cli_flags.md +0 -67
- package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
- package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
- package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
- package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
- /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
- /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { execDeployJson, execSfJson } = require('./utils');
|
|
5
|
+
|
|
6
|
+
const OBJECT_SOURCE_TARGET_MAPS_DIR = 'objectSourceTargetMaps';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Step 7: Deploy Accruals Reports with Data Space replacements.
|
|
10
|
+
*
|
|
11
|
+
* The Accruals Reports payload contains references between objects (DLOs/DLMs)
|
|
12
|
+
* and ObjectSourceTargetMap definitions. The maps reference fields that only
|
|
13
|
+
* exist after the other folders (objects, wave, ...) are deployed, so we must
|
|
14
|
+
* deploy them in two passes: first everything except objectSourceTargetMaps,
|
|
15
|
+
* then objectSourceTargetMaps on its own.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} orgAlias - The Salesforce org alias
|
|
18
|
+
* @param {string} setupDir - The setup directory path
|
|
19
|
+
* @param {boolean} skipDataSpace - Whether to skip data space configuration
|
|
20
|
+
* @param {string} configuredDataSpaceName - The data space name from config
|
|
21
|
+
* @param {string} configuredDataSpacePrefix - The data space prefix from config
|
|
22
|
+
* @param {string} namespace - Org namespace prefix (e.g. "cgcloud_dev__"), or '' for unmanaged orgs
|
|
23
|
+
* @param {boolean} dryRun - If true, skip deployment but keep temp files
|
|
24
|
+
*/
|
|
25
|
+
async function deployAccrualsReports(
|
|
26
|
+
orgAlias,
|
|
27
|
+
setupDir,
|
|
28
|
+
skipDataSpace,
|
|
29
|
+
configuredDataSpaceName,
|
|
30
|
+
configuredDataSpacePrefix,
|
|
31
|
+
namespace = '',
|
|
32
|
+
dryRun = false
|
|
33
|
+
) {
|
|
34
|
+
console.log('\n=== Step 7: Deploying Accruals Reports ===');
|
|
35
|
+
|
|
36
|
+
// Use "default" and empty prefix if skipping data space, otherwise use configured values
|
|
37
|
+
const dataSpaceName = skipDataSpace ? 'default' : configuredDataSpaceName;
|
|
38
|
+
const dataSpacePrefix = skipDataSpace ? '' : configuredDataSpacePrefix;
|
|
39
|
+
|
|
40
|
+
console.log(`Data Space Name: ${dataSpaceName}`);
|
|
41
|
+
console.log(`Data Space Prefix: ${dataSpacePrefix || '(empty)'}`);
|
|
42
|
+
console.log(`Org Namespace: ${namespace || '(none)'}`);
|
|
43
|
+
|
|
44
|
+
// setupDir is already the CGCloudAddons extraction dir (e.g. <tmp>/CGCloudAddons)
|
|
45
|
+
const sourceDir = path.join(setupDir, 'TPM', 'Accruals', 'Accruals Reports');
|
|
46
|
+
const tempDir = path.join(setupDir, 'temp-accruals-reports');
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(tempDir)) {
|
|
49
|
+
console.log('Cleaning up existing temp directory...');
|
|
50
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log('Copying Accruals Reports to temp directory...');
|
|
54
|
+
copyDirectory(sourceDir, tempDir);
|
|
55
|
+
|
|
56
|
+
console.log('Replacing Data Space placeholders and namespace in file names and contents...');
|
|
57
|
+
replaceDataSpacePlaceholders(tempDir, dataSpaceName, dataSpacePrefix, namespace);
|
|
58
|
+
|
|
59
|
+
const deployPath = path.join(tempDir, 'force-app');
|
|
60
|
+
const defaultDir = path.join(deployPath, 'main', 'default');
|
|
61
|
+
const objectSourceTargetMapsPath = path.join(defaultDir, OBJECT_SOURCE_TARGET_MAPS_DIR);
|
|
62
|
+
const hasMaps = fs.existsSync(objectSourceTargetMapsPath);
|
|
63
|
+
|
|
64
|
+
// objectSourceTargetMaps reference Data Cloud DLO/DLM objects by name.
|
|
65
|
+
//
|
|
66
|
+
// The template uses _ORG_NS_ (replaced above with the org namespace, e.g. "cgcloud__")
|
|
67
|
+
// and _DATASPACE_PREFIX_ (replaced with e.g. "DS2_").
|
|
68
|
+
//
|
|
69
|
+
// For DLO (__dll) and DLM (__dlm) object names and their field names, _ORG_NS_ is wrong:
|
|
70
|
+
// Data Cloud objects (MktDataLakeObject / MktDataModelObject) carry their OWN
|
|
71
|
+
// NamespacePrefix, which is independent of the org/package namespace. We query the
|
|
72
|
+
// actual DLO NamespacePrefix from the org at runtime and use that for correction.
|
|
73
|
+
//
|
|
74
|
+
// For targetObjectName the DataSpace prefix (DS2_) must be KEPT — it is part of the
|
|
75
|
+
// DataSpace-scoped DMO name (e.g. DS2_accrualoutput_decorated__dlm).
|
|
76
|
+
//
|
|
77
|
+
// Field names inside DLO/DLM references also need the DLO namespace, not the org namespace.
|
|
78
|
+
if (hasMaps) {
|
|
79
|
+
// Query the actual NamespacePrefix of the DLO from the org (may differ from org namespace)
|
|
80
|
+
let dloNamespace = '';
|
|
81
|
+
try {
|
|
82
|
+
const dloQuery = execSfJson([
|
|
83
|
+
'data', 'query', '--target-org', orgAlias, '--json',
|
|
84
|
+
'--query', "SELECT NamespacePrefix FROM MktDataLakeObject WHERE DeveloperName='accrualoutput_decorated' LIMIT 1"
|
|
85
|
+
]);
|
|
86
|
+
const dloRecs = (dloQuery.result && dloQuery.result.records) || [];
|
|
87
|
+
const raw = dloRecs.length > 0 ? (dloRecs[0].NamespacePrefix || '') : '';
|
|
88
|
+
dloNamespace = raw ? (raw.endsWith('__') ? raw : raw + '__') : '';
|
|
89
|
+
} catch (dloNsErr) {
|
|
90
|
+
// MktDataLakeObject may not be accessible in all org configurations — fall back to no DLO namespace.
|
|
91
|
+
// Log so operators can see the root cause rather than hitting a silent mis-mapping later.
|
|
92
|
+
console.warn(`[WARN] Could not query DLO namespace (falling back to empty): ${dloNsErr.message}`);
|
|
93
|
+
dloNamespace = '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The org namespace (applied by replaceDataSpacePlaceholders) that we need to undo on
|
|
97
|
+
// DLO/DLM names if it differs from the actual DLO namespace.
|
|
98
|
+
const appliedNs = namespace || ''; // what _ORG_NS_ was replaced with
|
|
99
|
+
|
|
100
|
+
const fixObjectName = (name) => {
|
|
101
|
+
let n = name;
|
|
102
|
+
// If the org namespace was applied to DLO/DLM names, undo it, then re-apply the
|
|
103
|
+
// correct DLO namespace (which may be different, e.g. '' for Data Cloud objects
|
|
104
|
+
// even in a namespaced org).
|
|
105
|
+
if (appliedNs && appliedNs !== dloNamespace) {
|
|
106
|
+
const escapedApplied = appliedNs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
107
|
+
// Undo on DLO/DLM object names: cgcloud__accrualX__dll → <dloNs>accrualX__dll
|
|
108
|
+
n = n.replace(new RegExp(escapedApplied + '(\\w+__dl[lm])', 'g'), dloNamespace + '$1');
|
|
109
|
+
// Undo on field names inside DLO/DLM refs: __dll.cgcloud__Field__c → __dll.<dloNs>Field__c
|
|
110
|
+
n = n.replace(new RegExp('(__dl[lm]\\.)' + escapedApplied + '(\\w+__c)', 'g'), '$1' + dloNamespace + '$2');
|
|
111
|
+
}
|
|
112
|
+
// NOTE: do NOT touch the DataSpace prefix on __dlm names.
|
|
113
|
+
// The MDAPI targetObjectName must include the DataSpace prefix (e.g. DS2_accrualoutput_decorated__dlm)
|
|
114
|
+
// to resolve to the DataSpace-scoped DMO. Removing it causes error 1923914934.
|
|
115
|
+
return n;
|
|
116
|
+
};
|
|
117
|
+
for (const entry of fs.readdirSync(objectSourceTargetMapsPath)) {
|
|
118
|
+
const fp = path.join(objectSourceTargetMapsPath, entry);
|
|
119
|
+
if (!fs.statSync(fp).isFile()) continue;
|
|
120
|
+
// Fix file contents: fix object/field names, then strip the filterOperationType
|
|
121
|
+
// tag from fieldSourceTargetMaps entries. That tag is present in the template but
|
|
122
|
+
// is not present in working org-deployed maps and causes an "unexpected error" on deploy.
|
|
123
|
+
let fileContent = fixObjectName(fs.readFileSync(fp, 'utf8'));
|
|
124
|
+
fileContent = fileContent.replace(
|
|
125
|
+
/\s*<filterOperationType>[^<]*<\/filterOperationType>/g, ''
|
|
126
|
+
);
|
|
127
|
+
fs.writeFileSync(fp, fileContent, 'utf8');
|
|
128
|
+
// Fix the filename: strip any leading DataSpace prefix from the stem so the
|
|
129
|
+
// MDAPI DeveloperName (= file stem) matches the masterLabel inside the XML.
|
|
130
|
+
// The template uses _DATASPACE_PREFIX_ as a file-naming hint; after replacement
|
|
131
|
+
// the file starts with e.g. "DS2_" but the DeveloperName should not have that.
|
|
132
|
+
let fixedName = entry;
|
|
133
|
+
if (dataSpacePrefix && fixedName.startsWith(dataSpacePrefix)) {
|
|
134
|
+
fixedName = fixedName.slice(dataSpacePrefix.length);
|
|
135
|
+
}
|
|
136
|
+
if (namespace && fixedName.startsWith(namespace)) {
|
|
137
|
+
fixedName = fixedName.slice(namespace.length);
|
|
138
|
+
}
|
|
139
|
+
if (fixedName !== entry) {
|
|
140
|
+
fs.renameSync(fp, path.join(objectSourceTargetMapsPath, fixedName));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (dryRun) {
|
|
146
|
+
console.log('[DRY RUN] Skipping deployment - files prepared at:');
|
|
147
|
+
console.log(` ${tempDir}`);
|
|
148
|
+
if (hasMaps) {
|
|
149
|
+
console.log(`[DRY RUN] objectSourceTargetMaps would deploy in a second pass from:`);
|
|
150
|
+
console.log(` ${objectSourceTargetMapsPath}`);
|
|
151
|
+
}
|
|
152
|
+
console.log('[DRY RUN] Temp directory preserved for inspection');
|
|
153
|
+
console.log('[OK] Step 7 completed (dry run)\n');
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Deployment order:
|
|
158
|
+
// Pass 1 — objects (DLM fields) only: must deploy before objectSourceTargetMaps
|
|
159
|
+
// (the maps reference fields that only exist once the DLM objects are in place).
|
|
160
|
+
// Wave (CRM Analytics) folder is staged aside so it doesn't cause
|
|
161
|
+
// "Not available for deploy" failures if CRM Analytics is not enabled.
|
|
162
|
+
// Pass 2 — objectSourceTargetMaps
|
|
163
|
+
// Pass 3 — wave (CRM Analytics dashboards), best-effort: warns but does not crash
|
|
164
|
+
// if CRM Analytics is not enabled in the org.
|
|
165
|
+
|
|
166
|
+
const wavePath = path.join(defaultDir, 'wave');
|
|
167
|
+
const hasWave = fs.existsSync(wavePath);
|
|
168
|
+
const stagedWavePath = path.join(tempDir, '__staged-wave');
|
|
169
|
+
|
|
170
|
+
// Stage wave aside so it doesn't contaminate the first two passes
|
|
171
|
+
if (hasWave) {
|
|
172
|
+
if (fs.existsSync(stagedWavePath)) fs.rmSync(stagedWavePath, { recursive: true });
|
|
173
|
+
console.log('Staging wave folder aside for the third deployment pass...');
|
|
174
|
+
fs.renameSync(wavePath, stagedWavePath);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
let stagedMapsPath = null;
|
|
178
|
+
if (hasMaps) {
|
|
179
|
+
stagedMapsPath = path.join(tempDir, '__staged-objectSourceTargetMaps');
|
|
180
|
+
if (fs.existsSync(stagedMapsPath)) fs.rmSync(stagedMapsPath, { recursive: true });
|
|
181
|
+
console.log('Setting aside objectSourceTargetMaps for the second deployment pass...');
|
|
182
|
+
fs.renameSync(objectSourceTargetMapsPath, stagedMapsPath);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Helper: convert tempDir source to MDAPI and deploy it, then clean up mdapi temp.
|
|
186
|
+
function convertAndDeploy(label) {
|
|
187
|
+
const mdapiOut = path.join(os.tmpdir(), `tpm_accruals_reports_mdapi_${label}_${Date.now()}`);
|
|
188
|
+
try {
|
|
189
|
+
const conv = execSfJson(['project', 'convert', 'source', '--output-dir', mdapiOut, '--json'], { cwd: tempDir });
|
|
190
|
+
if (conv.status !== 0) {
|
|
191
|
+
throw new Error(`Source conversion failed (${label}): ${conv.message || JSON.stringify(conv)}`);
|
|
192
|
+
}
|
|
193
|
+
execDeployJson(['project', 'deploy', 'start', '--metadata-dir', mdapiOut, '--target-org', orgAlias]);
|
|
194
|
+
} finally {
|
|
195
|
+
try { fs.rmSync(mdapiOut, { recursive: true }); } catch (cleanupErr) {
|
|
196
|
+
console.warn(`[WARN] Could not remove temp directory ${mdapiOut}: ${cleanupErr.message}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
// Pass 1: objects (DLM fields)
|
|
203
|
+
console.log('Deploying Accruals Reports metadata (pass 1: objects)...');
|
|
204
|
+
convertAndDeploy('pass1');
|
|
205
|
+
|
|
206
|
+
// Pass 2: objectSourceTargetMaps.
|
|
207
|
+
// The ObjectSourceTargetMap MDAPI deploy requires the DataSpace-scoped DMO
|
|
208
|
+
// (DS2_accrualoutput_decorated) to have IsEnabled=true in MktDataModelObject.
|
|
209
|
+
// The DataKit deployment leaves it as INACTIVE. We activate it here via
|
|
210
|
+
// the Tooling API before deploying the map.
|
|
211
|
+
if (stagedMapsPath) {
|
|
212
|
+
console.log('Restoring objectSourceTargetMaps for the second deployment pass...');
|
|
213
|
+
fs.renameSync(stagedMapsPath, objectSourceTargetMapsPath);
|
|
214
|
+
stagedMapsPath = null;
|
|
215
|
+
|
|
216
|
+
// Activate the DataSpace-scoped DMO (MktDataModelObject) so MDAPI can deploy the map.
|
|
217
|
+
if (!skipDataSpace && dataSpacePrefix) {
|
|
218
|
+
console.log('Activating DataSpace-scoped DMO for objectSourceTargetMaps deploy...');
|
|
219
|
+
// DMO must be active before MDAPI will accept the ObjectSourceTargetMap.
|
|
220
|
+
// If activation fails, the map deploy will fail with opaque error 1923914934 — throw early with a clear message.
|
|
221
|
+
activateDmoForDataSpace(orgAlias, dataSpacePrefix, namespace);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log('Deploying Accruals Reports metadata (pass 2: objectSourceTargetMaps)...');
|
|
225
|
+
try {
|
|
226
|
+
convertAndDeploy('pass2');
|
|
227
|
+
} catch (err) {
|
|
228
|
+
if (err.message && (err.message.includes('no CustomObject named') || err.message.includes('Not available for deploy'))) {
|
|
229
|
+
console.warn('[WARN] objectSourceTargetMaps deploy skipped — Data Cloud DLO/DLM objects not accessible via MDAPI in this org.');
|
|
230
|
+
console.warn('[WARN] This is expected when Data Cloud is not fully provisioned.');
|
|
231
|
+
console.warn(`[WARN] Original error: ${err.message.split('\n')[0]}`);
|
|
232
|
+
} else {
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
} finally {
|
|
238
|
+
// Restore any staged folders so the temp tree is consistent for inspection.
|
|
239
|
+
if (stagedMapsPath && fs.existsSync(stagedMapsPath) && !fs.existsSync(objectSourceTargetMapsPath)) {
|
|
240
|
+
fs.renameSync(stagedMapsPath, objectSourceTargetMapsPath);
|
|
241
|
+
}
|
|
242
|
+
if (hasWave && fs.existsSync(stagedWavePath) && !fs.existsSync(wavePath)) {
|
|
243
|
+
fs.renameSync(stagedWavePath, wavePath);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Pass 3: wave (CRM Analytics) — split into two sub-passes:
|
|
248
|
+
// 3a: WaveApplication (.wapp) + WaveDashboards (.wdash/.wdash-meta.xml) only
|
|
249
|
+
// These can deploy even before any data has flowed through the pipeline.
|
|
250
|
+
// 3b: WaveXmd (.xmd-meta.xml) best-effort
|
|
251
|
+
// XMDs annotate datasets that only exist after the pipeline has run once.
|
|
252
|
+
// They are expected to fail on fresh installs; this is non-fatal.
|
|
253
|
+
if (hasWave) {
|
|
254
|
+
// Move wave back into place for conversion
|
|
255
|
+
if (fs.existsSync(stagedWavePath)) fs.renameSync(stagedWavePath, wavePath);
|
|
256
|
+
// Stage everything except wave aside so only wave deploys
|
|
257
|
+
const stageForWave = path.join(tempDir, '__staged-non-wave');
|
|
258
|
+
if (fs.existsSync(stageForWave)) fs.rmSync(stageForWave, { recursive: true });
|
|
259
|
+
fs.mkdirSync(stageForWave, { recursive: true });
|
|
260
|
+
const defaultEntries = fs.readdirSync(defaultDir, { withFileTypes: true });
|
|
261
|
+
for (const e of defaultEntries) {
|
|
262
|
+
if (e.name !== 'wave') {
|
|
263
|
+
fs.renameSync(path.join(defaultDir, e.name), path.join(stageForWave, e.name));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Stage XMD files aside for pass 3b (they need datasets that don't exist yet on fresh install)
|
|
268
|
+
const xmdStageDir = path.join(tempDir, '__staged-xmd');
|
|
269
|
+
if (fs.existsSync(xmdStageDir)) fs.rmSync(xmdStageDir, { recursive: true });
|
|
270
|
+
fs.mkdirSync(xmdStageDir, { recursive: true });
|
|
271
|
+
const xmdFiles = fs.readdirSync(wavePath).filter(n => n.endsWith('.xmd-meta.xml'));
|
|
272
|
+
for (const xmd of xmdFiles) {
|
|
273
|
+
fs.renameSync(path.join(wavePath, xmd), path.join(xmdStageDir, xmd));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log('Deploying Accruals Reports metadata (pass 3a: WaveApplication + WaveDashboards)...');
|
|
277
|
+
let waveDeployOk = false;
|
|
278
|
+
try {
|
|
279
|
+
convertAndDeploy('pass3a-wave');
|
|
280
|
+
waveDeployOk = true;
|
|
281
|
+
} catch (err) {
|
|
282
|
+
if (err.message && err.message.includes('Not available for deploy for this organization')) {
|
|
283
|
+
console.warn('[WARN] Wave dashboards skipped — CRM Analytics is not enabled/licensed in this org.');
|
|
284
|
+
console.warn('[WARN] Enable Analytics Studio / CRM Analytics and re-run Step 7 to deploy the dashboards.');
|
|
285
|
+
} else {
|
|
286
|
+
console.warn(`[WARN] Wave dashboard deploy failed: ${err.message}`);
|
|
287
|
+
}
|
|
288
|
+
} finally {
|
|
289
|
+
// Restore staged non-wave folders
|
|
290
|
+
for (const e of fs.readdirSync(stageForWave, { withFileTypes: true })) {
|
|
291
|
+
const dest = path.join(defaultDir, e.name);
|
|
292
|
+
if (!fs.existsSync(dest)) {
|
|
293
|
+
fs.renameSync(path.join(stageForWave, e.name), dest);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
try { fs.rmSync(stageForWave, { recursive: true }); } catch (cleanupErr) {
|
|
297
|
+
console.warn(`[WARN] Could not remove temp staging directory: ${cleanupErr.message}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (waveDeployOk) {
|
|
302
|
+
console.log('[OK] Wave dashboards deployed.\n');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Pass 3b: XMDs — best-effort, expected to fail if datasets don't exist yet
|
|
306
|
+
if (xmdFiles.length > 0) {
|
|
307
|
+
// Restore XMDs into wave folder
|
|
308
|
+
for (const xmd of xmdFiles) {
|
|
309
|
+
fs.renameSync(path.join(xmdStageDir, xmd), path.join(wavePath, xmd));
|
|
310
|
+
}
|
|
311
|
+
// Stage all non-XMD wave files aside
|
|
312
|
+
const stageNonXmd = path.join(tempDir, '__staged-non-xmd');
|
|
313
|
+
if (fs.existsSync(stageNonXmd)) fs.rmSync(stageNonXmd, { recursive: true });
|
|
314
|
+
fs.mkdirSync(stageNonXmd, { recursive: true });
|
|
315
|
+
const waveEntries = fs.readdirSync(wavePath, { withFileTypes: true });
|
|
316
|
+
for (const e of waveEntries) {
|
|
317
|
+
if (!e.name.endsWith('.xmd-meta.xml')) {
|
|
318
|
+
fs.renameSync(path.join(wavePath, e.name), path.join(stageNonXmd, e.name));
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Also stage non-wave default dirs aside
|
|
322
|
+
const stageForXmd = path.join(tempDir, '__staged-non-wave-for-xmd');
|
|
323
|
+
if (fs.existsSync(stageForXmd)) fs.rmSync(stageForXmd, { recursive: true });
|
|
324
|
+
fs.mkdirSync(stageForXmd, { recursive: true });
|
|
325
|
+
const defaultEntriesForXmd = fs.readdirSync(defaultDir, { withFileTypes: true });
|
|
326
|
+
for (const e of defaultEntriesForXmd) {
|
|
327
|
+
if (e.name !== 'wave') {
|
|
328
|
+
fs.renameSync(path.join(defaultDir, e.name), path.join(stageForXmd, e.name));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
console.log('Deploying Accruals Reports metadata (pass 3b: WaveXmd, best-effort)...');
|
|
333
|
+
try {
|
|
334
|
+
convertAndDeploy('pass3b-xmd');
|
|
335
|
+
console.log('[OK] WaveXmd metadata deployed.\n');
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.warn('[WARN] WaveXmd deploy skipped — datasets not yet available (expected on fresh install).');
|
|
338
|
+
console.warn('[WARN] XMD files can be re-deployed after the accrual pipeline has run at least once.');
|
|
339
|
+
console.warn(`[WARN] Original error: ${err.message.split('\n')[0]}`);
|
|
340
|
+
} finally {
|
|
341
|
+
// Restore all staged items
|
|
342
|
+
for (const e of fs.readdirSync(stageNonXmd, { withFileTypes: true })) {
|
|
343
|
+
const dest = path.join(wavePath, e.name);
|
|
344
|
+
if (!fs.existsSync(dest)) fs.renameSync(path.join(stageNonXmd, e.name), dest);
|
|
345
|
+
}
|
|
346
|
+
try { fs.rmSync(stageNonXmd, { recursive: true }); } catch (_) {}
|
|
347
|
+
for (const e of fs.readdirSync(stageForXmd, { withFileTypes: true })) {
|
|
348
|
+
const dest = path.join(defaultDir, e.name);
|
|
349
|
+
if (!fs.existsSync(dest)) fs.renameSync(path.join(stageForXmd, e.name), dest);
|
|
350
|
+
}
|
|
351
|
+
try { fs.rmSync(stageForXmd, { recursive: true }); } catch (_) {}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
try { fs.rmSync(xmdStageDir, { recursive: true }); } catch (_) {}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
console.log('Cleaning up temp directory...');
|
|
358
|
+
try { fs.rmSync(tempDir, { recursive: true }); } catch (_) {}
|
|
359
|
+
|
|
360
|
+
console.log('[OK] Accruals Reports deployed successfully\n');
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Recursively copy a directory
|
|
365
|
+
*/
|
|
366
|
+
function copyDirectory(src, dest) {
|
|
367
|
+
if (!fs.existsSync(dest)) {
|
|
368
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
372
|
+
|
|
373
|
+
for (const entry of entries) {
|
|
374
|
+
const srcPath = path.join(src, entry.name);
|
|
375
|
+
const destPath = path.join(dest, entry.name);
|
|
376
|
+
|
|
377
|
+
if (entry.isDirectory()) {
|
|
378
|
+
copyDirectory(srcPath, destPath);
|
|
379
|
+
} else {
|
|
380
|
+
fs.copyFileSync(srcPath, destPath);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Replace placeholders in a name (file or directory)
|
|
387
|
+
* Handles double underscore pattern to avoid DS2__ becoming DS2___
|
|
388
|
+
*/
|
|
389
|
+
function replaceNamePlaceholders(name, dataSpaceName, dataSpacePrefix, namespace) {
|
|
390
|
+
let newName = name;
|
|
391
|
+
if (dataSpacePrefix === '') {
|
|
392
|
+
newName = newName.replace(/_DATASPACE_PREFIX__/g, '');
|
|
393
|
+
} else {
|
|
394
|
+
newName = newName.replace(/_DATASPACE_PREFIX__/g, dataSpacePrefix);
|
|
395
|
+
}
|
|
396
|
+
newName = newName
|
|
397
|
+
.replace(/_DATASPACE_PREFIX_/g, dataSpacePrefix)
|
|
398
|
+
.replace(/_DATASPACE_NAME_/g, dataSpaceName)
|
|
399
|
+
.replace(/_ORG_NS_/g, namespace || '');
|
|
400
|
+
return newName;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Replace Data Space placeholders in both file names and file contents
|
|
405
|
+
*/
|
|
406
|
+
function replaceDataSpacePlaceholders(dir, dataSpaceName, dataSpacePrefix, namespace) {
|
|
407
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
408
|
+
|
|
409
|
+
for (const entry of entries) {
|
|
410
|
+
const oldPath = path.join(dir, entry.name);
|
|
411
|
+
|
|
412
|
+
if (entry.isDirectory()) {
|
|
413
|
+
replaceDataSpacePlaceholders(oldPath, dataSpaceName, dataSpacePrefix, namespace);
|
|
414
|
+
|
|
415
|
+
if (entry.name.includes('_DATASPACE_PREFIX_') || entry.name.includes('_DATASPACE_NAME_') || entry.name.includes('_ORG_NS_')) {
|
|
416
|
+
const newName = replaceNamePlaceholders(entry.name, dataSpaceName, dataSpacePrefix, namespace);
|
|
417
|
+
const newPath = path.join(dir, newName);
|
|
418
|
+
fs.renameSync(oldPath, newPath);
|
|
419
|
+
}
|
|
420
|
+
} else {
|
|
421
|
+
replaceInFile(oldPath, dataSpaceName, dataSpacePrefix, namespace);
|
|
422
|
+
|
|
423
|
+
if (entry.name.includes('_DATASPACE_PREFIX_') || entry.name.includes('_DATASPACE_NAME_') || entry.name.includes('_ORG_NS_')) {
|
|
424
|
+
const newName = replaceNamePlaceholders(entry.name, dataSpaceName, dataSpacePrefix, namespace);
|
|
425
|
+
const newPath = path.join(dir, newName);
|
|
426
|
+
fs.renameSync(oldPath, newPath);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Replace placeholders in file contents
|
|
434
|
+
*/
|
|
435
|
+
function replaceInFile(filePath, dataSpaceName, dataSpacePrefix, namespace) {
|
|
436
|
+
try {
|
|
437
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
438
|
+
|
|
439
|
+
if (content.includes('_DATASPACE_PREFIX_') || content.includes('_DATASPACE_NAME_') || content.includes('_ORG_NS_')) {
|
|
440
|
+
if (dataSpacePrefix === '') {
|
|
441
|
+
// If dataspace prefix is empty, remove double underscore pattern entirely
|
|
442
|
+
content = content.replace(/_DATASPACE_PREFIX__/g, '');
|
|
443
|
+
} else {
|
|
444
|
+
// Replace double underscore pattern first to avoid double underscores in output
|
|
445
|
+
// _DATASPACE_PREFIX__ with DS2_ results in DS2_ (not DS2__)
|
|
446
|
+
content = content.replace(/_DATASPACE_PREFIX__/g, dataSpacePrefix);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
content = content
|
|
450
|
+
.replace(/_DATASPACE_PREFIX_/g, dataSpacePrefix)
|
|
451
|
+
.replace(/_DATASPACE_NAME_/g, dataSpaceName)
|
|
452
|
+
.replace(/_ORG_NS_/g, namespace || '');
|
|
453
|
+
|
|
454
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
455
|
+
}
|
|
456
|
+
} catch (error) {
|
|
457
|
+
if (error.code === 'EISDIR') {
|
|
458
|
+
// Directory entry — skip silently, expected when walking mixed trees
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
// Unexpected I/O failure (missing file, permission error, write error)
|
|
462
|
+
throw new Error(`Failed to process template file ${filePath}: ${error.message}`, { cause: error });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Activate the DataSpace-scoped DMO (MktDataModelObject) via the Tooling API.
|
|
468
|
+
*
|
|
469
|
+
* The DataKit deployment creates the DS2_<name> DMO in INACTIVE state. MDAPI refuses to
|
|
470
|
+
* deploy an ObjectSourceTargetMap for an INACTIVE DMO (opaque error 1923914934). This
|
|
471
|
+
* function queries the MktDataModelObject by DeveloperName and sets IsEnabled=true via PATCH.
|
|
472
|
+
*/
|
|
473
|
+
function activateDmoForDataSpace(orgAlias, dataSpacePrefix, namespace) {
|
|
474
|
+
// DeveloperName = dataSpacePrefix (without trailing _) + '_accrualoutput_decorated'
|
|
475
|
+
const pfxBase = dataSpacePrefix.replace(/_$/, '');
|
|
476
|
+
const dmoDevName = `${pfxBase}_accrualoutput_decorated`;
|
|
477
|
+
|
|
478
|
+
// Query via sf data query --use-tooling-api — CLI supplies auth, no token extraction.
|
|
479
|
+
// Returns: status=0 on success, result.records as array.
|
|
480
|
+
const queryResult = execSfJson([
|
|
481
|
+
'data', 'query',
|
|
482
|
+
'--query', `SELECT Id,IsEnabled,DataModelObjectStatus FROM MktDataModelObject WHERE DeveloperName='${dmoDevName}'`,
|
|
483
|
+
'--use-tooling-api', '--target-org', orgAlias, '--json'
|
|
484
|
+
]);
|
|
485
|
+
const records = queryResult?.result?.records;
|
|
486
|
+
if (queryResult?.status !== 0 || !Array.isArray(records)) {
|
|
487
|
+
throw new Error(`Tooling query for DMO '${dmoDevName}' failed: ${queryResult?.message || JSON.stringify(queryResult)}`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (records.length === 0) {
|
|
491
|
+
console.warn(`[WARN] DMO '${dmoDevName}' not found — skipping activation.`);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const { Id: dmoId, IsEnabled, DataModelObjectStatus } = records[0];
|
|
496
|
+
if (IsEnabled && DataModelObjectStatus === 'ACTIVE') {
|
|
497
|
+
console.log(`DMO '${dmoDevName}' already active — skipping.`);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
console.log(`Activating DMO '${dmoDevName}' (was IsEnabled=${IsEnabled}, status=${DataModelObjectStatus})...`);
|
|
502
|
+
// Update via sf data update record --use-tooling-api — CLI supplies auth, no token extraction.
|
|
503
|
+
// Returns: status=0 on success, result.success=true.
|
|
504
|
+
const updateResult = execSfJson([
|
|
505
|
+
'data', 'update', 'record',
|
|
506
|
+
'--sobject', 'MktDataModelObject',
|
|
507
|
+
'--record-id', dmoId,
|
|
508
|
+
'--values', `IsEnabled=true DataModelObjectStatus=ACTIVE RefEntityDeveloperName=accrualoutput_decorated`,
|
|
509
|
+
'--use-tooling-api', '--target-org', orgAlias, '--json'
|
|
510
|
+
]);
|
|
511
|
+
if (updateResult?.status !== 0 || !updateResult?.result?.success) {
|
|
512
|
+
throw new Error(`Tooling update for DMO '${dmoDevName}' failed: ${updateResult?.message || JSON.stringify(updateResult?.result)}`);
|
|
513
|
+
}
|
|
514
|
+
console.log(`[OK] DMO '${dmoDevName}' activated.`);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
module.exports = deployAccrualsReports;
|