@salesforce/afv-skills 1.47.0 → 1.48.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/education-cloud-domain-configure/SKILL.md +234 -0
- package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
- package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
- package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
- package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
- package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
- package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
- package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
- package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
- package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +4 -3
- package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
- package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
- package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
- package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
- package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
- package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
- package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
- package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
- package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
- package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
- package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
- package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
- package/skills/service-omni-agent-users-create/SKILL.md +96 -0
- package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
- package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
- package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
- package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
- package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
- package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
- package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
- package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
- package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
- package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
- package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
- package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
- package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
- package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
- package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
- package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
- package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
- package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
- package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
- package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
- package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
- package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
- package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
- package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
- package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
- package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
- package/skills/service-omni-queue-deploy/SKILL.md +91 -0
- package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
- package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
- package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
- package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
- package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
- package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
- package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
- package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
- package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
- package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
- package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
- package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
- package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
- package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
- package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
- package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
- package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
- package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
- package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
- package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
- package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
- package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
- package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
- package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
- package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
- package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
- package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
- package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
- package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
- package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
- package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
- package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
- package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
- package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
- package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
- package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# deploy-and-report.sh - deploy + verify an Omni routing Flow: autolaunched dryRun smoke flow (default, verified via Actions REST) or record-triggered production flow (--trigger, token-resolved, optional --runtime-proof). Args, token resolution, exit codes: SKILL.md.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# Escape the five XML predefined entities so a ServiceChannel MasterLabel with & < > " ' cannot
|
|
7
|
+
# corrupt the flow XML when substituted into the asset below.
|
|
8
|
+
xml_escape() {
|
|
9
|
+
local s="$1"
|
|
10
|
+
s="${s//&/&}"; s="${s//</<}"; s="${s//>/>}"; s="${s//\"/"}"; s="${s//\'/'}"
|
|
11
|
+
printf '%s' "$s"
|
|
12
|
+
}
|
|
13
|
+
# Escape sed replacement metacharacters (\ & and the | delimiter) so an escaped value is inserted
|
|
14
|
+
# literally by the sed substitution.
|
|
15
|
+
sed_repl_escape() { printf '%s' "$1" | sed -e 's/[\\&|]/\\&/g'; }
|
|
16
|
+
|
|
17
|
+
if [ $# -lt 1 ]; then
|
|
18
|
+
echo '{"error":"Missing required argument: org-alias","usage":"bash deploy-and-report.sh <org-alias> [flow_developer_name] [--skip-invoke] [--target Case|VoiceCall] [--trigger] [--runtime-proof]"}' >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# --- Parse flags out of the arg list, leaving positional args intact ---
|
|
23
|
+
SKIP_INVOKE="false"
|
|
24
|
+
TRIGGER_MODE="false"
|
|
25
|
+
RUNTIME_PROOF="false"
|
|
26
|
+
# --require-proof (or OMNI_RUNTIME_PROOF_REQUIRED=1): make runtime routing proof mandatory (a missing
|
|
27
|
+
# PendingServiceRouting|AgentWork blocks instead of fail-soft); the Voice release gate. Implies --runtime-proof.
|
|
28
|
+
REQUIRE_PROOF="${OMNI_RUNTIME_PROOF_REQUIRED:-0}"
|
|
29
|
+
[ "$REQUIRE_PROOF" = "1" ] && { REQUIRE_PROOF="true"; RUNTIME_PROOF="true"; TRIGGER_MODE="true"; } || REQUIRE_PROOF="false"
|
|
30
|
+
TARGET="Case"
|
|
31
|
+
# Routing type for the record-triggered production flow: QueueBased (default, backward-compatible) or
|
|
32
|
+
# SkillsBased. SkillsBased selects the skills-based flow variant (${FLOW_DN}.SkillsBased.flow-meta.xml)
|
|
33
|
+
# and REQUIRES a non-null skillOption - a null skillOption makes the platform routeWork action NPE and
|
|
34
|
+
# roll back the triggering insert (W-24069761).
|
|
35
|
+
ROUTING_TYPE="${OMNI_ROUTING_TYPE:-QueueBased}"
|
|
36
|
+
# skillOption for SkillsBased routing (ignored for QueueBased). RunSBRRules (default) makes the platform
|
|
37
|
+
# evaluate the org's WorkSkillRouting rules server-side and attach matching SkillRequirement rows;
|
|
38
|
+
# DefineSkillRequirements uses only skills the flow passes; Both does both. Closed set - mirrors
|
|
39
|
+
# OmniFlowConstants.SkillOption in core.
|
|
40
|
+
SKILL_OPTION="${OMNI_SKILL_OPTION:-RunSBRRules}"
|
|
41
|
+
SKILL_OPTION_EXPLICIT="false"
|
|
42
|
+
POSITIONAL=()
|
|
43
|
+
while [ $# -gt 0 ]; do
|
|
44
|
+
case "$1" in
|
|
45
|
+
--skip-invoke) SKIP_INVOKE="true" ;;
|
|
46
|
+
--trigger) TRIGGER_MODE="true" ;;
|
|
47
|
+
--runtime-proof) RUNTIME_PROOF="true"; TRIGGER_MODE="true" ;;
|
|
48
|
+
--require-proof) REQUIRE_PROOF="true"; RUNTIME_PROOF="true"; TRIGGER_MODE="true" ;;
|
|
49
|
+
--target) shift; TARGET="${1:-Case}" ;;
|
|
50
|
+
--target=*) TARGET="${1#*=}" ;;
|
|
51
|
+
--routing-type) shift; ROUTING_TYPE="${1:-}" ;;
|
|
52
|
+
--routing-type=*) ROUTING_TYPE="${1#*=}" ;;
|
|
53
|
+
--skill-option) shift; SKILL_OPTION="${1:-}"; SKILL_OPTION_EXPLICIT="true" ;;
|
|
54
|
+
--skill-option=*) SKILL_OPTION="${1#*=}"; SKILL_OPTION_EXPLICIT="true" ;;
|
|
55
|
+
*) POSITIONAL+=("$1") ;;
|
|
56
|
+
esac
|
|
57
|
+
shift
|
|
58
|
+
done
|
|
59
|
+
set -- "${POSITIONAL[@]}"
|
|
60
|
+
|
|
61
|
+
ORG="$1"
|
|
62
|
+
|
|
63
|
+
case "$TARGET" in
|
|
64
|
+
Case) DEFAULT_SMOKE_FLOW="Omni_Route_Cases"; DEFAULT_TRIGGER_FLOW="Omni_Route_Case_Trigger"; DEFAULT_SC_DEVNAME="Cases" ;;
|
|
65
|
+
VoiceCall) DEFAULT_SMOKE_FLOW="Omni_Route_VoiceCalls"; DEFAULT_TRIGGER_FLOW="Omni_Route_VoiceCall_Trigger"; DEFAULT_SC_DEVNAME="sfdc_phone" ;;
|
|
66
|
+
*) echo "{\"error\":\"Invalid --target '$TARGET'. Supported: Case, VoiceCall\"}" >&2; exit 2 ;;
|
|
67
|
+
esac
|
|
68
|
+
|
|
69
|
+
case "$ROUTING_TYPE" in
|
|
70
|
+
QueueBased|SkillsBased) ;;
|
|
71
|
+
*) echo "{\"error\":\"Invalid --routing-type '$ROUTING_TYPE'. Supported: QueueBased, SkillsBased\"}" >&2; exit 2 ;;
|
|
72
|
+
esac
|
|
73
|
+
# 761 guard: a SkillsBased routeWork with a null/invalid skillOption NPEs in the platform action and
|
|
74
|
+
# rolls back the triggering insert. Refuse to emit one - the skill never ships a null skillOption.
|
|
75
|
+
if [ "$ROUTING_TYPE" = "SkillsBased" ]; then
|
|
76
|
+
case "$SKILL_OPTION" in
|
|
77
|
+
RunSBRRules|DefineSkillRequirements|Both) ;;
|
|
78
|
+
"") echo "{\"error\":\"routing-type SkillsBased requires a non-empty --skill-option (RunSBRRules|DefineSkillRequirements|Both). Refusing to deploy a SkillsBased routeWork flow with a null skillOption: it would NPE in the platform action and roll back the triggering insert (W-24069761).\"}" >&2; exit 2 ;;
|
|
79
|
+
*) echo "{\"error\":\"Invalid --skill-option '$SKILL_OPTION'. Supported: RunSBRRules, DefineSkillRequirements, Both\"}" >&2; exit 2 ;;
|
|
80
|
+
esac
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [ "$ROUTING_TYPE" = "QueueBased" ] && [ "$SKILL_OPTION_EXPLICIT" = "true" ]; then
|
|
84
|
+
echo '{"error":"--skill-option is valid only with --routing-type SkillsBased."}' >&2
|
|
85
|
+
exit 2
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# Flow to operate on: explicit positional wins; else target+mode default.
|
|
89
|
+
if [ "$TRIGGER_MODE" = "true" ]; then
|
|
90
|
+
FLOW_DN="${2:-$DEFAULT_TRIGGER_FLOW}"
|
|
91
|
+
else
|
|
92
|
+
FLOW_DN="${2:-$DEFAULT_SMOKE_FLOW}"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if ! [[ "$FLOW_DN" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
96
|
+
echo "{\"error\":\"Invalid flow_developer_name '$FLOW_DN'. Must start with a letter, only A-Z a-z 0-9 _ (max 80).\"}" >&2
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
if ! [[ "$ORG" =~ ^[A-Za-z0-9._@-]{1,128}$ ]]; then
|
|
101
|
+
echo "{\"error\":\"Invalid org alias '$ORG'.\"}" >&2
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
|
|
106
|
+
echo "{\"error\":\"Org alias '$ORG' is not authenticated.\"}" >&2
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
# Write guard: deploys Flow metadata, so self-enforce the production-org guard (directly invokable).
|
|
111
|
+
ORG_GUARD_JSON=$(sf data query --target-org "$ORG" \
|
|
112
|
+
--query "SELECT IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" \
|
|
113
|
+
--json 2>/dev/null) || {
|
|
114
|
+
echo "{\"error\":\"Failed to query Organization for safe_to_write guard: $(printf '%s' "$ORG_GUARD_JSON" | head -c 300)\"}" >&2
|
|
115
|
+
exit 1
|
|
116
|
+
}
|
|
117
|
+
IS_SANDBOX=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].IsSandbox')
|
|
118
|
+
TRIAL_EXP=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
|
|
119
|
+
ORG_TYPE=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].OrganizationType')
|
|
120
|
+
|
|
121
|
+
SAFE_TO_WRITE=false
|
|
122
|
+
if [ "$IS_SANDBOX" = "true" ] \
|
|
123
|
+
|| [ "$TRIAL_EXP" != "null" ] \
|
|
124
|
+
|| [ "$ORG_TYPE" = "Developer Edition" ] \
|
|
125
|
+
|| [ "$ORG_TYPE" = "Base Edition" ]; then
|
|
126
|
+
SAFE_TO_WRITE=true
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
if [ "$SAFE_TO_WRITE" != "true" ]; then
|
|
130
|
+
echo "{\"skill\":\"service-omni-routing-flow-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Refusing to deploy Flow metadata to a real production customer org (IsSandbox=$IS_SANDBOX, TrialExpirationDate=$TRIAL_EXP, OrganizationType=$ORG_TYPE). Target a sandbox, trial CDO, or Developer Edition org.\",\"safe_to_write\":false}" >&2
|
|
131
|
+
exit 1
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
135
|
+
ASSETS_DIR="$(cd "$SCRIPT_DIR/../assets" && pwd)"
|
|
136
|
+
FLOW_XML="$ASSETS_DIR/force-app/main/default/flows/${FLOW_DN}.flow-meta.xml"
|
|
137
|
+
# Template the deploy reads from. The SkillsBased trigger variant is a sibling asset carrying
|
|
138
|
+
# routingType=SkillsBased + a non-null skillOption; QueueBased and the smoke/dryRun flows use the base
|
|
139
|
+
# asset. Either way the temp copy is written as ${FLOW_DN}.flow-meta.xml, so the deployed DeveloperName
|
|
140
|
+
# stays $FLOW_DN (the SkillsBased flow REPLACES the QueueBased one - never two triggers on one object).
|
|
141
|
+
TEMPLATE_XML="$FLOW_XML"
|
|
142
|
+
if [ "$TRIGGER_MODE" = "true" ] && [ "$ROUTING_TYPE" = "SkillsBased" ]; then
|
|
143
|
+
TEMPLATE_XML="$ASSETS_DIR/force-app/main/default/flows/${FLOW_DN}.SkillsBased.flow-meta.xml"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
if [ ! -f "$ASSETS_DIR/sfdx-project.json" ]; then
|
|
147
|
+
echo "{\"error\":\"Assets DX project not found at $ASSETS_DIR\"}" >&2
|
|
148
|
+
exit 1
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
if [ ! -f "$TEMPLATE_XML" ]; then
|
|
152
|
+
echo "{\"error\":\"Flow asset not found at $TEMPLATE_XML. Add the .flow-meta.xml file, or pick a different flow_developer_name / --routing-type.\"}" >&2
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Resolve the org's API version so we hit the right Actions REST endpoint. The Flow metadata
|
|
157
|
+
# is written at 66.0 but the org may serve a newer API - the endpoint takes any recent version.
|
|
158
|
+
API_VERSION=$(sf org display --target-org "$ORG" --json 2>/dev/null \
|
|
159
|
+
| jq -r '.result.apiVersion // "66.0"')
|
|
160
|
+
if ! [[ "$API_VERSION" =~ ^[0-9]+\.[0-9]+$ ]]; then
|
|
161
|
+
API_VERSION="66.0"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# TRIGGER MODE - deploy the record-triggered production flow (token-resolved), then optionally prove
|
|
165
|
+
# routing at runtime. Self-contained: emits its own report and exits.
|
|
166
|
+
if [ "$TRIGGER_MODE" = "true" ]; then
|
|
167
|
+
# Cleanup on EVERY exit path: remove the temp DX project + delete any throwaway proof record.
|
|
168
|
+
TWORK=""
|
|
169
|
+
CLEANUP_RECORD_ID=""
|
|
170
|
+
_rf_cleanup() {
|
|
171
|
+
[ -n "$TWORK" ] && rm -rf "$TWORK" 2>/dev/null || true
|
|
172
|
+
if [ -n "$CLEANUP_RECORD_ID" ]; then
|
|
173
|
+
sf data delete record --target-org "$ORG" --sobject "$TARGET" --record-id "$CLEANUP_RECORD_ID" >/dev/null 2>&1 || true
|
|
174
|
+
fi
|
|
175
|
+
}
|
|
176
|
+
trap _rf_cleanup EXIT
|
|
177
|
+
|
|
178
|
+
trigger_blocked() {
|
|
179
|
+
jq -n --arg flow "$FLOW_DN" --arg target "$TARGET" --arg msg "$1" \
|
|
180
|
+
'{skill:"service-omni-routing-flow-deploy", status:"blocked", mode:"record_triggered",
|
|
181
|
+
target:$target, flow_developer_name:$flow, flow_active:false,
|
|
182
|
+
runtime_proof:{attempted:false,success:false,reason:"blocked_before_proof"},
|
|
183
|
+
manual_actions:[], blocking_issue:$msg}'
|
|
184
|
+
exit 1
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# --- Resolve the Queue bound to the target sObject ---
|
|
188
|
+
QDN="${QUEUE_DEVELOPER_NAME:-}"
|
|
189
|
+
if [ -n "$QDN" ] && ! [[ "$QDN" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
190
|
+
trigger_blocked "Invalid QUEUE_DEVELOPER_NAME '$QDN'."
|
|
191
|
+
fi
|
|
192
|
+
if [ -z "$QDN" ]; then
|
|
193
|
+
QSO=$(sf data query --target-org "$ORG" \
|
|
194
|
+
--query "SELECT QueueId, Queue.DeveloperName FROM QueueSobject WHERE SobjectType='$TARGET' ORDER BY Queue.DeveloperName" --json 2>/dev/null)
|
|
195
|
+
QSO_DNS=$(echo "$QSO" | jq -c '[.result.records[].Queue.DeveloperName] | unique' 2>/dev/null || echo '[]')
|
|
196
|
+
QSO_N=$(echo "$QSO_DNS" | jq 'length' 2>/dev/null || echo 0)
|
|
197
|
+
if [ "$QSO_N" = "0" ]; then
|
|
198
|
+
trigger_blocked "No queue is bound to '$TARGET'. Run service-omni-queue-deploy --create-if-missing first, or set QUEUE_DEVELOPER_NAME."
|
|
199
|
+
elif [ "$QSO_N" != "1" ]; then
|
|
200
|
+
trigger_blocked "Multiple queues bound to '$TARGET' ($QSO_DNS). Set QUEUE_DEVELOPER_NAME to disambiguate."
|
|
201
|
+
fi
|
|
202
|
+
QDN=$(echo "$QSO_DNS" | jq -r '.[0]')
|
|
203
|
+
fi
|
|
204
|
+
Q_JSON=$(sf data query --target-org "$ORG" \
|
|
205
|
+
--query "SELECT Id, QueueRoutingConfigId FROM Group WHERE DeveloperName='$QDN' AND Type='Queue'" --json 2>/dev/null)
|
|
206
|
+
QUEUE_ID=$(echo "$Q_JSON" | jq -r '.result.records[0].Id // empty')
|
|
207
|
+
[ -z "$QUEUE_ID" ] && trigger_blocked "Queue '$QDN' not found on org."
|
|
208
|
+
QUEUE_RC_ID=$(echo "$Q_JSON" | jq -r '.result.records[0].QueueRoutingConfigId // empty')
|
|
209
|
+
|
|
210
|
+
# --- Resolve the QueueRoutingConfig ---
|
|
211
|
+
RC_DN="${ROUTING_CONFIG_DEVELOPER_NAME:-}"
|
|
212
|
+
if [ -n "$RC_DN" ] && ! [[ "$RC_DN" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
213
|
+
trigger_blocked "Invalid ROUTING_CONFIG_DEVELOPER_NAME '$RC_DN'."
|
|
214
|
+
fi
|
|
215
|
+
if [ -z "$RC_DN" ] && [ -n "$QUEUE_RC_ID" ]; then
|
|
216
|
+
RC_DN=$(sf data query --target-org "$ORG" \
|
|
217
|
+
--query "SELECT DeveloperName FROM QueueRoutingConfig WHERE Id='$QUEUE_RC_ID'" --json 2>/dev/null \
|
|
218
|
+
| jq -r '.result.records[0].DeveloperName // empty')
|
|
219
|
+
fi
|
|
220
|
+
if [ -z "$RC_DN" ]; then
|
|
221
|
+
case "$TARGET" in
|
|
222
|
+
VoiceCall) RC_DN="Voice_Routing_Config" ;;
|
|
223
|
+
*) RC_DN="Case_Routing_Config" ;;
|
|
224
|
+
esac
|
|
225
|
+
fi
|
|
226
|
+
RC_ID=$(sf data query --target-org "$ORG" \
|
|
227
|
+
--query "SELECT Id FROM QueueRoutingConfig WHERE DeveloperName='$RC_DN'" --json 2>/dev/null \
|
|
228
|
+
| jq -r '.result.records[0].Id // empty')
|
|
229
|
+
[ -z "$RC_ID" ] && trigger_blocked "QueueRoutingConfig '$RC_DN' does not exist. Run service-omni-queue-routing-config-deploy (QRC_ROUTING_TARGET=$TARGET) first, or set ROUTING_CONFIG_DEVELOPER_NAME."
|
|
230
|
+
|
|
231
|
+
# --- Resolve the ServiceChannel (Tooling API), FAIL CLOSED if absent ---
|
|
232
|
+
# The flow embeds the ServiceChannel Id in routeWork; an empty id would deploy a flow routing
|
|
233
|
+
# nothing. The coordinator passes the discovered channel via SERVICE_CHANNEL_DEVELOPER_NAME.
|
|
234
|
+
SC_DN="${SERVICE_CHANNEL_DEVELOPER_NAME:-$DEFAULT_SC_DEVNAME}"
|
|
235
|
+
if [ -n "$SC_DN" ] && ! [[ "$SC_DN" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
236
|
+
trigger_blocked "Invalid SERVICE_CHANNEL_DEVELOPER_NAME '$SC_DN'."
|
|
237
|
+
fi
|
|
238
|
+
SC_TOOLING=$(sf data query --target-org "$ORG" --use-tooling-api \
|
|
239
|
+
--query "SELECT Id, MasterLabel FROM ServiceChannel WHERE DeveloperName='$SC_DN'" --json 2>/dev/null || echo '{}')
|
|
240
|
+
SC_ID=$(echo "$SC_TOOLING" | jq -r '.result.records[0].Id // empty')
|
|
241
|
+
SC_LABEL=$(echo "$SC_TOOLING" | jq -r '.result.records[0].MasterLabel // empty')
|
|
242
|
+
if [ -z "$SC_ID" ]; then
|
|
243
|
+
trigger_blocked "ServiceChannel '$SC_DN' not found on org (Tooling API returned no row). For $TARGET run service-omni-service-channel-configure (sobject_type=$TARGET) first - the standard Voice channel is 'sfdc_phone' - or set SERVICE_CHANNEL_DEVELOPER_NAME to an existing channel. Refusing to deploy a flow with an unresolved ServiceChannel."
|
|
244
|
+
fi
|
|
245
|
+
[ -z "$SC_LABEL" ] && SC_LABEL="$SC_DN"
|
|
246
|
+
|
|
247
|
+
# --- PLAN_ONLY: report the resolution, do not write ---
|
|
248
|
+
if [ "${PLAN_ONLY:-}" = "1" ]; then
|
|
249
|
+
jq -n --arg flow "$FLOW_DN" --arg target "$TARGET" --arg q "$QDN" --arg rc "$RC_DN" --arg sc "$SC_DN" \
|
|
250
|
+
--arg routing_type "$ROUTING_TYPE" --arg skill_option "$SKILL_OPTION" \
|
|
251
|
+
'{skill:"service-omni-routing-flow-deploy", status:"action_needed", plan_mode:true, mode:"record_triggered",
|
|
252
|
+
target:$target, flow_developer_name:$flow, routing_type:$routing_type,
|
|
253
|
+
skill_option:(if $routing_type=="SkillsBased" then $skill_option else null end),
|
|
254
|
+
resolved:{queue:$q, routing_config:$rc, service_channel:$sc},
|
|
255
|
+
plan_detail:("Would deploy record-triggered \($routing_type) flow \($flow) binding queue \($q), QRC \($rc), channel \($sc)" + (if $routing_type=="SkillsBased" then " with skillOption \($skill_option)." else "." end)),
|
|
256
|
+
manual_actions:[], blocking_issue:null}'
|
|
257
|
+
exit 0
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
# --- Idempotency guard with stale-binding detection ---
|
|
261
|
+
# A Flow deploy always mints a new version, so re-runs would grow the version count. If an active
|
|
262
|
+
# version exists AND its embedded queue/QRC/channel Ids still match, report "reused" and skip;
|
|
263
|
+
# if the bindings have drifted, redeploy. OMNI_FLOW_FORCE=1 always redeploys.
|
|
264
|
+
EXISTING_ACTIVE=$(sf data query --target-org "$ORG" --use-tooling-api \
|
|
265
|
+
--query "SELECT Id FROM Flow WHERE Definition.DeveloperName='$FLOW_DN' AND Status='Active' LIMIT 1" --json 2>/dev/null \
|
|
266
|
+
| jq -r '.result.records[0].Id // empty')
|
|
267
|
+
BINDINGS_MATCH="false"
|
|
268
|
+
if [ -n "$EXISTING_ACTIVE" ]; then
|
|
269
|
+
RWORK="$(mktemp -d)"
|
|
270
|
+
cp "$ASSETS_DIR/sfdx-project.json" "$RWORK/sfdx-project.json"
|
|
271
|
+
mkdir -p "$RWORK/force-app/main/default"
|
|
272
|
+
( cd "$RWORK" && sf project retrieve start --target-org "$ORG" --metadata "Flow:${FLOW_DN}" --json >/dev/null 2>&1 || true )
|
|
273
|
+
ACTIVE_XML=$(find "$RWORK" -name "${FLOW_DN}.flow-meta.xml" 2>/dev/null | head -1)
|
|
274
|
+
if [ -n "$ACTIVE_XML" ] && [ -f "$ACTIVE_XML" ]; then
|
|
275
|
+
# Resource bindings and routing semantics must all match. Without the routing-mode checks, a
|
|
276
|
+
# QueueBased flow with current IDs could be incorrectly reused for a SkillsBased request.
|
|
277
|
+
if grep -qF "$QUEUE_ID" "$ACTIVE_XML" 2>/dev/null \
|
|
278
|
+
&& grep -qF "$RC_ID" "$ACTIVE_XML" 2>/dev/null \
|
|
279
|
+
&& grep -qF "$SC_ID" "$ACTIVE_XML" 2>/dev/null \
|
|
280
|
+
&& grep -qF "<stringValue>${ROUTING_TYPE}</stringValue>" "$ACTIVE_XML" 2>/dev/null \
|
|
281
|
+
&& { [ "$ROUTING_TYPE" != "SkillsBased" ] \
|
|
282
|
+
|| grep -qF "<stringValue>${SKILL_OPTION}</stringValue>" "$ACTIVE_XML" 2>/dev/null; }; then
|
|
283
|
+
BINDINGS_MATCH="true"
|
|
284
|
+
fi
|
|
285
|
+
fi
|
|
286
|
+
rm -rf "$RWORK"
|
|
287
|
+
fi
|
|
288
|
+
if [ -n "$EXISTING_ACTIVE" ] && [ "$BINDINGS_MATCH" = "true" ] && [ "${OMNI_FLOW_FORCE:-0}" != "1" ]; then
|
|
289
|
+
TRIG_STATUS="reused"
|
|
290
|
+
TSTATE="Unchanged"
|
|
291
|
+
else
|
|
292
|
+
# --- Substitute tokens into a temp copy of the asset and deploy ---
|
|
293
|
+
TWORK="$(mktemp -d)"
|
|
294
|
+
mkdir -p "$TWORK/force-app/main/default/flows"
|
|
295
|
+
cp "$ASSETS_DIR/sfdx-project.json" "$TWORK/sfdx-project.json"
|
|
296
|
+
SC_LABEL_SED="$(sed_repl_escape "$(xml_escape "$SC_LABEL")")"
|
|
297
|
+
sed \
|
|
298
|
+
-e "s|__SERVICE_CHANNEL_ID__|${SC_ID}|g" \
|
|
299
|
+
-e "s|__SERVICE_CHANNEL_DEVNAME__|${SC_DN}|g" \
|
|
300
|
+
-e "s|__SERVICE_CHANNEL_LABEL__|${SC_LABEL_SED}|g" \
|
|
301
|
+
-e "s|__ROUTING_CONFIG_ID__|${RC_ID}|g" \
|
|
302
|
+
-e "s|__QUEUE_ID__|${QUEUE_ID}|g" \
|
|
303
|
+
-e "s|__SKILL_OPTION__|${SKILL_OPTION}|g" \
|
|
304
|
+
"$TEMPLATE_XML" > "$TWORK/force-app/main/default/flows/${FLOW_DN}.flow-meta.xml"
|
|
305
|
+
|
|
306
|
+
TDEPLOY=$(cd "$TWORK" && sf project deploy start --target-org "$ORG" --metadata "Flow:${FLOW_DN}" --json 2>/dev/null || true)
|
|
307
|
+
TDEPLOY_OK=$(echo "$TDEPLOY" | jq -r '.result.success // false' 2>/dev/null)
|
|
308
|
+
if [ "$TDEPLOY_OK" != "true" ]; then
|
|
309
|
+
TERR=$(echo "$TDEPLOY" | jq -r '.result.details.componentFailures // [] | map(.problem) | join("; ")' 2>/dev/null)
|
|
310
|
+
[ -z "$TERR" ] || [ "$TERR" = "null" ] && TERR=$(echo "$TDEPLOY" | jq -r '.message // "Unknown deploy error"' 2>/dev/null)
|
|
311
|
+
trigger_blocked "Record-triggered flow deploy failed: $TERR"
|
|
312
|
+
fi
|
|
313
|
+
TSTATE=$(echo "$TDEPLOY" | jq -r --arg fn "$FLOW_DN" '.result.files[] | select(.fullName==$fn and .type=="Flow") | .state' 2>/dev/null | head -1)
|
|
314
|
+
TSTATE="${TSTATE:-unknown}"
|
|
315
|
+
TRIG_STATUS="reused"
|
|
316
|
+
case "$TSTATE" in Created) TRIG_STATUS="created";; Changed) TRIG_STATUS="updated";; Unchanged) TRIG_STATUS="reused";; *) trigger_blocked "Deploy ok but Flow state unrecognized ('$TSTATE').";; esac
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# --- Round-trip active-version proof ---
|
|
320
|
+
# Component state only proves the deploy transaction; a flow with no active version never fires.
|
|
321
|
+
# Prefer FlowDefinitionView, then fall back to the Tooling Flow row because an unrelated malformed
|
|
322
|
+
# managed-package definition can make FlowDefinitionView fail for the entire org.
|
|
323
|
+
TRIG_ACTIVE_VER=$(sf data query --target-org "$ORG" \
|
|
324
|
+
--query "SELECT ApiName, ActiveVersionId FROM FlowDefinitionView WHERE ApiName='$FLOW_DN'" --json 2>/dev/null \
|
|
325
|
+
| jq -r '.result.records[0].ActiveVersionId // ""' 2>/dev/null || echo "")
|
|
326
|
+
if [ -z "$TRIG_ACTIVE_VER" ] || [ "$TRIG_ACTIVE_VER" = "null" ]; then
|
|
327
|
+
TRIG_ACTIVE_VER=$(sf data query --target-org "$ORG" --use-tooling-api \
|
|
328
|
+
--query "SELECT Id FROM Flow WHERE Definition.DeveloperName='$FLOW_DN' AND Status='Active' LIMIT 1" --json 2>/dev/null \
|
|
329
|
+
| jq -r '.result.records[0].Id // ""' 2>/dev/null || echo "")
|
|
330
|
+
fi
|
|
331
|
+
if [ -z "$TRIG_ACTIVE_VER" ] || [ "$TRIG_ACTIVE_VER" = "null" ]; then
|
|
332
|
+
trigger_blocked "Record-triggered flow '$FLOW_DN' deployed (state=$TSTATE), but neither FlowDefinitionView nor the Tooling Flow query found an active version, so routing would not fire. Re-run, or activate the flow in Setup → Flows."
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
# --- Runtime proof (optional): fire the trigger and look for a PendingServiceRouting/AgentWork ---
|
|
336
|
+
RP_ATTEMPTED="false"; RP_SUCCESS="false"; RP_REASON=""; RP_RECORD_ID=""; RP_PSR="null"; RP_AW="null"
|
|
337
|
+
# SkillsBased acceptance signal: SkillRequirement rows the platform attached to the PSR (the exact
|
|
338
|
+
# thing that was missing when routeWork hardcoded QueueBased - W-24069467).
|
|
339
|
+
RP_SKILLREQS="null"; RP_SKILLREQ_COUNT="0"
|
|
340
|
+
if [ "$RUNTIME_PROOF" = "true" ]; then
|
|
341
|
+
RP_ATTEMPTED="true"
|
|
342
|
+
case "$TARGET" in
|
|
343
|
+
Case) REC=$(sf data create record --target-org "$ORG" --sobject Case --values "Subject='Omni routing proof' Status='New' Origin='Phone'" --json 2>/dev/null || true) ;;
|
|
344
|
+
VoiceCall) REC=$(sf data create record --target-org "$ORG" --sobject VoiceCall --values "CallType='Inbound'" --json 2>/dev/null || true) ;;
|
|
345
|
+
esac
|
|
346
|
+
RP_RECORD_ID=$(echo "$REC" | jq -r '.result.id // empty' 2>/dev/null)
|
|
347
|
+
# Register the record for deletion on exit (trap) so the proof never leaves test data behind.
|
|
348
|
+
[ -n "$RP_RECORD_ID" ] && CLEANUP_RECORD_ID="$RP_RECORD_ID"
|
|
349
|
+
if [ -z "$RP_RECORD_ID" ]; then
|
|
350
|
+
RP_REASON="Could not create a $TARGET record to fire the trigger: $(echo "$REC" | jq -r '.message // (.result.errors[0].message) // "unknown"' 2>/dev/null | head -c 240). On orgs without Service Cloud Voice provisioned, VoiceCall is not directly insertable - deploy is proven, runtime proof is not."
|
|
351
|
+
else
|
|
352
|
+
# Poll for PendingServiceRouting then AgentWork (async). Cadence env-tunable
|
|
353
|
+
# (OMNI_PROOF_POLL_TRIES / OMNI_PROOF_POLL_SLEEP); defaults ~30s.
|
|
354
|
+
RP_TRIES="${OMNI_PROOF_POLL_TRIES:-6}"
|
|
355
|
+
RP_SLEEP="${OMNI_PROOF_POLL_SLEEP:-5}"
|
|
356
|
+
for _ in $(seq 1 "$RP_TRIES"); do
|
|
357
|
+
PSR=$(sf data query --target-org "$ORG" \
|
|
358
|
+
--query "SELECT Id, IsReadyForRouting, RoutingModel FROM PendingServiceRouting WHERE WorkItemId='$RP_RECORD_ID' ORDER BY CreatedDate DESC LIMIT 1" --json 2>/dev/null || echo '{}')
|
|
359
|
+
if [ "$(echo "$PSR" | jq -r '.result.totalSize // 0')" != "0" ]; then
|
|
360
|
+
RP_PSR=$(echo "$PSR" | jq -c '.result.records[0]')
|
|
361
|
+
if [ "$ROUTING_TYPE" = "SkillsBased" ]; then
|
|
362
|
+
_psr_id=$(echo "$PSR" | jq -r '.result.records[0].Id')
|
|
363
|
+
# SkillRequirement has no SkillLevel column; its skill-strength field is SkillNumber.
|
|
364
|
+
SREQ=$(sf data query --target-org "$ORG" \
|
|
365
|
+
--query "SELECT Id, SkillId, SkillNumber, IsAdditionalSkill, SkillPriority FROM SkillRequirement WHERE RelatedRecordId='$_psr_id'" --json 2>/dev/null || true)
|
|
366
|
+
# A failed query prints error JSON to stdout THEN exits non-zero, so a `|| echo '{}'`
|
|
367
|
+
# fallback would concatenate two JSON values and break the downstream --argjson. Validate
|
|
368
|
+
# that we captured a single well-formed value and fall back to empty otherwise.
|
|
369
|
+
echo "$SREQ" | jq -e . >/dev/null 2>&1 || SREQ='{}'
|
|
370
|
+
RP_SKILLREQ_COUNT=$(echo "$SREQ" | jq -r '.result.totalSize // 0' 2>/dev/null || echo 0)
|
|
371
|
+
RP_SKILLREQS=$(echo "$SREQ" | jq -c '.result.records // []' 2>/dev/null || echo '[]')
|
|
372
|
+
if [ "$RP_SKILLREQ_COUNT" -gt 0 ]; then
|
|
373
|
+
RP_SUCCESS="true"
|
|
374
|
+
break
|
|
375
|
+
fi
|
|
376
|
+
else
|
|
377
|
+
RP_SUCCESS="true"
|
|
378
|
+
break
|
|
379
|
+
fi
|
|
380
|
+
fi
|
|
381
|
+
AW=$(sf data query --target-org "$ORG" \
|
|
382
|
+
--query "SELECT Id, Status FROM AgentWork WHERE WorkItemId='$RP_RECORD_ID' ORDER BY CreatedDate DESC LIMIT 1" --json 2>/dev/null || echo '{}')
|
|
383
|
+
if [ "$(echo "$AW" | jq -r '.result.totalSize // 0')" != "0" ]; then
|
|
384
|
+
RP_AW=$(echo "$AW" | jq -c '.result.records[0]')
|
|
385
|
+
if [ "$ROUTING_TYPE" != "SkillsBased" ]; then
|
|
386
|
+
RP_SUCCESS="true"
|
|
387
|
+
break
|
|
388
|
+
fi
|
|
389
|
+
fi
|
|
390
|
+
sleep "$RP_SLEEP"
|
|
391
|
+
done
|
|
392
|
+
if [ "$RP_SUCCESS" != "true" ] && [ "$ROUTING_TYPE" = "SkillsBased" ] \
|
|
393
|
+
&& [ "$RP_PSR" != "null" ] && [ "$RP_SKILLREQ_COUNT" = "0" ]; then
|
|
394
|
+
RP_REASON="A PendingServiceRouting row appeared for record $RP_RECORD_ID, but it has no SkillRequirement rows. SkillsBased runtime proof requires at least one SkillRequirement produced by the active WorkSkillRouting rule."
|
|
395
|
+
elif [ "$RP_SUCCESS" != "true" ]; then
|
|
396
|
+
RP_REASON="Record $RP_RECORD_ID created but no PendingServiceRouting/AgentWork appeared within the proof window. The flow deployed and is Active; routing may require an online agent, a configured Contact Center, or more time."
|
|
397
|
+
fi
|
|
398
|
+
fi
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
# Mandatory-proof gate: when proof is required, a missing PendingServiceRouting|AgentWork blocks.
|
|
402
|
+
if [ "$REQUIRE_PROOF" = "true" ] && [ "$RP_SUCCESS" != "true" ]; then
|
|
403
|
+
jq -n \
|
|
404
|
+
--arg flow "$FLOW_DN" --arg target "$TARGET" --arg state "$TSTATE" --arg active_ver "$TRIG_ACTIVE_VER" \
|
|
405
|
+
--arg routing_type "$ROUTING_TYPE" --arg skill_option "$SKILL_OPTION" \
|
|
406
|
+
--arg q "$QDN" --arg qid "$QUEUE_ID" --arg rc "$RC_DN" --arg rcid "$RC_ID" --arg sc "$SC_DN" --arg scid "$SC_ID" \
|
|
407
|
+
--arg rp_rec "$RP_RECORD_ID" --argjson rp_psr "$RP_PSR" --argjson rp_aw "$RP_AW" --arg rp_reason "$RP_REASON" \
|
|
408
|
+
--argjson rp_skillreqs "$RP_SKILLREQS" --arg rp_skillreq_count "$RP_SKILLREQ_COUNT" \
|
|
409
|
+
'{skill:"service-omni-routing-flow-deploy", status:"blocked", mode:"record_triggered", target:$target,
|
|
410
|
+
flow_developer_name:$flow, flow_state:$state, flow_active:true, flow_active_version_id:$active_ver,
|
|
411
|
+
routing_type:$routing_type, skill_option:(if $routing_type=="SkillsBased" then $skill_option else null end),
|
|
412
|
+
resolved:{queue:{developer_name:$q,id:$qid}, routing_config:{developer_name:$rc,id:$rcid}, service_channel:{developer_name:$sc,id:(if $scid=="" then null else $scid end)}},
|
|
413
|
+
runtime_proof:{attempted:true, required:true, success:false, record_id:(if $rp_rec=="" then null else $rp_rec end), pending_service_routing:$rp_psr, agent_work:$rp_aw, skill_requirement_count:($rp_skillreq_count|tonumber), skill_requirements:(if $routing_type=="SkillsBased" then $rp_skillreqs else null end), reason:(if $rp_reason=="" then null else $rp_reason end)},
|
|
414
|
+
manual_actions:[], blocking_issue:("Runtime routing proof REQUIRED but not observed: " + (if $rp_reason=="" then "no PendingServiceRouting/AgentWork appeared" else $rp_reason end) + " (VoiceCall runtime proof needs a provisioned Contact Center + an online agent).")}'
|
|
415
|
+
exit 1
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
jq -n \
|
|
419
|
+
--arg status "$TRIG_STATUS" --arg flow "$FLOW_DN" --arg target "$TARGET" --arg state "$TSTATE" --arg active_ver "$TRIG_ACTIVE_VER" \
|
|
420
|
+
--arg routing_type "$ROUTING_TYPE" --arg skill_option "$SKILL_OPTION" \
|
|
421
|
+
--arg q "$QDN" --arg qid "$QUEUE_ID" --arg rc "$RC_DN" --arg rcid "$RC_ID" --arg sc "$SC_DN" --arg scid "$SC_ID" \
|
|
422
|
+
--arg rp_attempted "$RP_ATTEMPTED" --arg rp_success "$RP_SUCCESS" --arg rp_reason "$RP_REASON" \
|
|
423
|
+
--arg rp_required "$REQUIRE_PROOF" \
|
|
424
|
+
--arg rp_rec "$RP_RECORD_ID" --argjson rp_psr "$RP_PSR" --argjson rp_aw "$RP_AW" \
|
|
425
|
+
--argjson rp_skillreqs "$RP_SKILLREQS" --arg rp_skillreq_count "$RP_SKILLREQ_COUNT" \
|
|
426
|
+
'{
|
|
427
|
+
skill:"service-omni-routing-flow-deploy",
|
|
428
|
+
status:$status,
|
|
429
|
+
mode:"record_triggered",
|
|
430
|
+
target:$target,
|
|
431
|
+
flow_developer_name:$flow,
|
|
432
|
+
flow_state:$state,
|
|
433
|
+
flow_active:true,
|
|
434
|
+
flow_active_version_id:$active_ver,
|
|
435
|
+
routing_type:$routing_type,
|
|
436
|
+
skill_option:(if $routing_type=="SkillsBased" then $skill_option else null end),
|
|
437
|
+
resolved:{queue:{developer_name:$q, id:$qid}, routing_config:{developer_name:$rc, id:$rcid}, service_channel:{developer_name:$sc, id:(if $scid=="" then null else $scid end)}},
|
|
438
|
+
runtime_proof:{
|
|
439
|
+
attempted:($rp_attempted=="true"),
|
|
440
|
+
required:($rp_required=="true"),
|
|
441
|
+
success:($rp_success=="true"),
|
|
442
|
+
record_id:(if $rp_rec=="" then null else $rp_rec end),
|
|
443
|
+
pending_service_routing:$rp_psr,
|
|
444
|
+
agent_work:$rp_aw,
|
|
445
|
+
skill_requirement_count:($rp_skillreq_count|tonumber),
|
|
446
|
+
skill_requirements:(if $routing_type=="SkillsBased" then $rp_skillreqs else null end),
|
|
447
|
+
reason:(if $rp_reason=="" then null else $rp_reason end)
|
|
448
|
+
},
|
|
449
|
+
manual_actions:[],
|
|
450
|
+
blocking_issue:null
|
|
451
|
+
}'
|
|
452
|
+
exit 0
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
# Deploy - explicit --metadata (not --source-dir) so a sibling metadata file can't piggyback.
|
|
456
|
+
DEPLOY_JSON=$(cd "$ASSETS_DIR" && sf project deploy start --target-org "$ORG" \
|
|
457
|
+
--metadata "Flow:${FLOW_DN}" \
|
|
458
|
+
--json 2>/dev/null || true)
|
|
459
|
+
|
|
460
|
+
DEPLOY_SUCCESS=$(echo "$DEPLOY_JSON" | jq -r '.result.success // false' 2>/dev/null)
|
|
461
|
+
DEPLOY_ID=$(echo "$DEPLOY_JSON" | jq -r '.result.id // null' 2>/dev/null)
|
|
462
|
+
|
|
463
|
+
translate_deploy_error() {
|
|
464
|
+
local raw="$1"
|
|
465
|
+
case "$raw" in
|
|
466
|
+
*INVALID_TYPE*"Flow"*)
|
|
467
|
+
echo "Flow metadata type not enabled on this org (unusual - usually indicates the org edition doesn't include Flow Builder)."
|
|
468
|
+
;;
|
|
469
|
+
*"Flow.*is not valid for this API version"*|*"API version"*)
|
|
470
|
+
echo "Flow XML apiVersion or asset version mismatch. Confirm assets/force-app/main/default/flows/${FLOW_DN}.flow-meta.xml <apiVersion> is <= the org's current API version ($API_VERSION)."
|
|
471
|
+
;;
|
|
472
|
+
*"Invalid element name"*|*"was not expected"*)
|
|
473
|
+
echo "Flow XML schema mismatch. The Flow metadata schema is strict; check element ordering and spelling against v66 Metadata API docs."
|
|
474
|
+
;;
|
|
475
|
+
*INSUFFICIENT_ACCESS*)
|
|
476
|
+
echo "Executing user lacks Metadata API deploy permissions. Re-authenticate as a System Administrator or grant Author Apex + Manage Flows."
|
|
477
|
+
;;
|
|
478
|
+
*)
|
|
479
|
+
echo "$raw"
|
|
480
|
+
;;
|
|
481
|
+
esac
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if [ "$DEPLOY_SUCCESS" != "true" ]; then
|
|
485
|
+
FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.result.details.componentFailures // [] | map(.problem) | join("; ")' 2>/dev/null)
|
|
486
|
+
if [ -z "$FAILURES" ] || [ "$FAILURES" = "null" ]; then
|
|
487
|
+
FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.message // .result.errorMessage // "Unknown deploy error"' 2>/dev/null)
|
|
488
|
+
fi
|
|
489
|
+
FRIENDLY=$(translate_deploy_error "$FAILURES")
|
|
490
|
+
|
|
491
|
+
jq -n \
|
|
492
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
493
|
+
--arg flow "$FLOW_DN" \
|
|
494
|
+
--arg blocking "$FRIENDLY" \
|
|
495
|
+
--arg raw "$FAILURES" \
|
|
496
|
+
'{
|
|
497
|
+
skill: "service-omni-routing-flow-deploy",
|
|
498
|
+
status: "blocked",
|
|
499
|
+
flow_developer_name: $flow,
|
|
500
|
+
flow_state: "not_deployed",
|
|
501
|
+
flow_active: false,
|
|
502
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
503
|
+
invocation_smoke_test: {attempted: false, success: false, reason: "deploy_failed"},
|
|
504
|
+
manual_actions: [],
|
|
505
|
+
blocking_issue: $blocking,
|
|
506
|
+
raw_error: $raw
|
|
507
|
+
}'
|
|
508
|
+
exit 1
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
FLOW_STATE=$(echo "$DEPLOY_JSON" | jq -r --arg fn "$FLOW_DN" \
|
|
512
|
+
'.result.files[] | select(.fullName == $fn and .type == "Flow") | .state' 2>/dev/null | head -1)
|
|
513
|
+
FLOW_STATE="${FLOW_STATE:-unknown}"
|
|
514
|
+
|
|
515
|
+
case "$FLOW_STATE" in
|
|
516
|
+
Created|Changed|Unchanged) ;;
|
|
517
|
+
*)
|
|
518
|
+
jq -n \
|
|
519
|
+
--arg flow "$FLOW_DN" \
|
|
520
|
+
--arg st "$FLOW_STATE" \
|
|
521
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
522
|
+
'{
|
|
523
|
+
skill: "service-omni-routing-flow-deploy",
|
|
524
|
+
status: "blocked",
|
|
525
|
+
flow_developer_name: $flow,
|
|
526
|
+
flow_state: $st,
|
|
527
|
+
flow_active: false,
|
|
528
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
529
|
+
invocation_smoke_test: {attempted: false, success: false, reason: "unrecognized_component_state"},
|
|
530
|
+
manual_actions: [],
|
|
531
|
+
blocking_issue: ("Deploy reported success overall but Flow component has an unrecognized/absent state \"" + $st + "\" (expected Created, Changed, or Unchanged).")
|
|
532
|
+
}'
|
|
533
|
+
exit 1
|
|
534
|
+
;;
|
|
535
|
+
esac
|
|
536
|
+
|
|
537
|
+
# Post-verify #1: retrieve the deployed Flow and confirm <status>Active</status> (a flow can deploy
|
|
538
|
+
# in Draft/Obsolete - a silent failure, since autolaunched flows must be Active to be CLI-invocable).
|
|
539
|
+
VERIFY_WORK="$(mktemp -d)"
|
|
540
|
+
trap 'rm -rf "$VERIFY_WORK"' EXIT
|
|
541
|
+
cp "$ASSETS_DIR/sfdx-project.json" "$VERIFY_WORK/sfdx-project.json"
|
|
542
|
+
mkdir -p "$VERIFY_WORK/force-app/main/default"
|
|
543
|
+
# Retrieve for its side effect (writes the flow XML); verified by inspecting the file below.
|
|
544
|
+
( cd "$VERIFY_WORK" && sf project retrieve start --target-org "$ORG" \
|
|
545
|
+
--metadata "Flow:${FLOW_DN}" --json >/dev/null 2>&1 || true )
|
|
546
|
+
RETRIEVED_XML=$(find "$VERIFY_WORK" -name "${FLOW_DN}.flow-meta.xml" 2>/dev/null | head -1)
|
|
547
|
+
|
|
548
|
+
FLOW_ACTIVE="unverified"
|
|
549
|
+
if [ -n "$RETRIEVED_XML" ] && [ -f "$RETRIEVED_XML" ]; then
|
|
550
|
+
if grep -qE "<status>Active</status>" "$RETRIEVED_XML"; then
|
|
551
|
+
FLOW_ACTIVE="true"
|
|
552
|
+
else
|
|
553
|
+
FLOW_ACTIVE="false"
|
|
554
|
+
fi
|
|
555
|
+
fi
|
|
556
|
+
|
|
557
|
+
if [ "$FLOW_ACTIVE" != "true" ]; then
|
|
558
|
+
if [ "$FLOW_ACTIVE" = "false" ]; then
|
|
559
|
+
BLOCK_MSG="Flow deployed but not Active on post-verify (the retrieved metadata does not carry <status>Active</status>). The flow XML must declare <status>Active</status> for autolaunched CLI invocation to work."
|
|
560
|
+
else
|
|
561
|
+
BLOCK_MSG="Flow deployed but the Active-status verification could not be performed (retrieve returned no Flow XML - inconclusive)."
|
|
562
|
+
fi
|
|
563
|
+
jq -n \
|
|
564
|
+
--arg flow "$FLOW_DN" \
|
|
565
|
+
--arg st "$FLOW_STATE" \
|
|
566
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
567
|
+
--arg active "$FLOW_ACTIVE" \
|
|
568
|
+
--arg msg "$BLOCK_MSG" \
|
|
569
|
+
'{
|
|
570
|
+
skill: "service-omni-routing-flow-deploy",
|
|
571
|
+
status: "blocked",
|
|
572
|
+
flow_developer_name: $flow,
|
|
573
|
+
flow_state: $st,
|
|
574
|
+
flow_active: ($active == "true"),
|
|
575
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
576
|
+
invocation_smoke_test: {attempted: false, success: false, reason: "flow_not_active"},
|
|
577
|
+
manual_actions: [],
|
|
578
|
+
blocking_issue: $msg
|
|
579
|
+
}'
|
|
580
|
+
exit 1
|
|
581
|
+
fi
|
|
582
|
+
|
|
583
|
+
# Post-verify #2: invoke the flow via Actions REST with dryRun=true (proves CLI-executability).
|
|
584
|
+
# POST /services/data/vXX.X/actions/custom/flow/<FlowDN>, body {"inputs":[{"dryRun":true}]};
|
|
585
|
+
# success = [0].isSuccess && [0].outputValues.dryRunOk.
|
|
586
|
+
INVOKE_ATTEMPTED="false"
|
|
587
|
+
INVOKE_SUCCESS="false"
|
|
588
|
+
INVOKE_REASON=""
|
|
589
|
+
INVOKE_RAW="{}"
|
|
590
|
+
INVOKE_DRY_RUN_OK="false"
|
|
591
|
+
|
|
592
|
+
if [ "$SKIP_INVOKE" != "true" ]; then
|
|
593
|
+
INVOKE_ATTEMPTED="true"
|
|
594
|
+
INVOKE_ENDPOINT="/services/data/v${API_VERSION}/actions/custom/flow/${FLOW_DN}"
|
|
595
|
+
INVOKE_BODY='{"inputs":[{"dryRun":true}]}'
|
|
596
|
+
|
|
597
|
+
INVOKE_RAW=$(sf api request rest \
|
|
598
|
+
--method POST \
|
|
599
|
+
--target-org "$ORG" \
|
|
600
|
+
--body "$INVOKE_BODY" \
|
|
601
|
+
"$INVOKE_ENDPOINT" \
|
|
602
|
+
2>&1 || true)
|
|
603
|
+
|
|
604
|
+
# sf api request rest (beta) prints a warning line before the JSON body; strip everything up to
|
|
605
|
+
# the first line starting with [ or { - that's the actual body.
|
|
606
|
+
INVOKE_JSON=$(printf '%s' "$INVOKE_RAW" | awk '/^[[{]/,0')
|
|
607
|
+
|
|
608
|
+
# Parse the stripped payload (raw array for Actions API, or an error-wrapping object).
|
|
609
|
+
if echo "$INVOKE_JSON" | jq -e '.' >/dev/null 2>&1; then
|
|
610
|
+
IS_SUCCESS=$(echo "$INVOKE_JSON" | jq -r 'if type == "array" then .[0].isSuccess else .isSuccess // false end' 2>/dev/null)
|
|
611
|
+
INVOKE_DRY_RUN_OK=$(echo "$INVOKE_JSON" | jq -r 'if type == "array" then (.[0].outputValues.dryRunOk // false) else (.outputValues.dryRunOk // false) end' 2>/dev/null)
|
|
612
|
+
if [ "$IS_SUCCESS" = "true" ] && [ "$INVOKE_DRY_RUN_OK" = "true" ]; then
|
|
613
|
+
INVOKE_SUCCESS="true"
|
|
614
|
+
else
|
|
615
|
+
INVOKE_REASON=$(echo "$INVOKE_JSON" | jq -r '
|
|
616
|
+
if type == "array" then
|
|
617
|
+
(.[0].errors // []) | map(.message) | join("; ")
|
|
618
|
+
else
|
|
619
|
+
(.errors // []) | map(.message) | join("; ")
|
|
620
|
+
end' 2>/dev/null)
|
|
621
|
+
if [ -z "$INVOKE_REASON" ] || [ "$INVOKE_REASON" = "null" ]; then
|
|
622
|
+
INVOKE_REASON="Invocation returned isSuccess=$IS_SUCCESS, dryRunOk=$INVOKE_DRY_RUN_OK (expected true/true)."
|
|
623
|
+
fi
|
|
624
|
+
fi
|
|
625
|
+
else
|
|
626
|
+
INVOKE_REASON="Actions API returned non-JSON after stripping the sf beta-warning preamble. Raw first 200 chars: $(printf '%s' "$INVOKE_RAW" | head -c 200)"
|
|
627
|
+
fi
|
|
628
|
+
|
|
629
|
+
if [ "$INVOKE_SUCCESS" != "true" ]; then
|
|
630
|
+
jq -n \
|
|
631
|
+
--arg flow "$FLOW_DN" \
|
|
632
|
+
--arg st "$FLOW_STATE" \
|
|
633
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
634
|
+
--arg reason "$INVOKE_REASON" \
|
|
635
|
+
--argjson raw "$(echo "$INVOKE_JSON" | jq -R -s '.' 2>/dev/null || echo '""')" \
|
|
636
|
+
'{
|
|
637
|
+
skill: "service-omni-routing-flow-deploy",
|
|
638
|
+
status: "blocked",
|
|
639
|
+
flow_developer_name: $flow,
|
|
640
|
+
flow_state: $st,
|
|
641
|
+
flow_active: true,
|
|
642
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
643
|
+
invocation_smoke_test: {attempted: true, success: false, reason: $reason, raw_response: $raw},
|
|
644
|
+
manual_actions: [{id: "INVOCATION_FAILED", title: "Flow deployed & Active but Actions REST invocation failed. Rerun with --skip-invoke to accept deploy-only, or investigate the API response.", endpoint: ("/services/data/v" + "'"$API_VERSION"'" + "/actions/custom/flow/" + $flow)}],
|
|
645
|
+
blocking_issue: ("Flow deployed and Active, but invocation via Actions REST failed: " + $reason)
|
|
646
|
+
}'
|
|
647
|
+
exit 1
|
|
648
|
+
fi
|
|
649
|
+
fi
|
|
650
|
+
|
|
651
|
+
# Skill-level status.
|
|
652
|
+
STATUS="reused"
|
|
653
|
+
case "$FLOW_STATE" in
|
|
654
|
+
Created) STATUS="created" ;;
|
|
655
|
+
Changed) STATUS="updated" ;;
|
|
656
|
+
Unchanged) STATUS="reused" ;;
|
|
657
|
+
esac
|
|
658
|
+
|
|
659
|
+
jq -n \
|
|
660
|
+
--arg status "$STATUS" \
|
|
661
|
+
--arg flow "$FLOW_DN" \
|
|
662
|
+
--arg flow_state "$FLOW_STATE" \
|
|
663
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
664
|
+
--arg api_ver "$API_VERSION" \
|
|
665
|
+
--arg invoke_attempted "$INVOKE_ATTEMPTED" \
|
|
666
|
+
--arg invoke_success "$INVOKE_SUCCESS" \
|
|
667
|
+
--arg dry_run_ok "$INVOKE_DRY_RUN_OK" \
|
|
668
|
+
'{
|
|
669
|
+
skill: "service-omni-routing-flow-deploy",
|
|
670
|
+
status: $status,
|
|
671
|
+
flow_developer_name: $flow,
|
|
672
|
+
flow_state: $flow_state,
|
|
673
|
+
flow_active: true,
|
|
674
|
+
deploy_id: $deploy_id,
|
|
675
|
+
invocation_smoke_test: {
|
|
676
|
+
attempted: ($invoke_attempted == "true"),
|
|
677
|
+
success: ($invoke_success == "true"),
|
|
678
|
+
cli_path: "actions_rest",
|
|
679
|
+
endpoint: ("/services/data/v" + $api_ver + "/actions/custom/flow/" + $flow),
|
|
680
|
+
dry_run_ok: ($dry_run_ok == "true")
|
|
681
|
+
},
|
|
682
|
+
manual_actions: [],
|
|
683
|
+
blocking_issue: null
|
|
684
|
+
}'
|