mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-03 21:56:02 +08:00
509 lines
14 KiB
Markdown
509 lines
14 KiB
Markdown
<!-- Parent: omnistudio-dependencies-analyze/SKILL.md -->
|
|
|
|
# OmniStudio Dependency Patterns
|
|
|
|
## Overview
|
|
|
|
OmniStudio components form a directed graph where each component type can reference others. Dependencies are not stored in lookup fields — they are embedded in JSON configuration fields (`PropertySetConfig`, `Definition`, `InputObjectName`/`OutputObjectName`). Extracting dependencies requires parsing these JSON structures.
|
|
|
|
---
|
|
|
|
## Dependency Direction Summary
|
|
|
|
```
|
|
OmniScript ──→ Integration Procedure (via IP Action element)
|
|
OmniScript ──→ Data Mapper (via DataRaptor Action element)
|
|
OmniScript ──→ OmniScript (via embedded OmniScript element)
|
|
OmniScript ──→ Apex Class (via Remote Action element)
|
|
OmniScript ──→ LWC (via Custom Lightning Web Component element)
|
|
OmniScript ──→ HTTP Endpoint (via HTTP Action element)
|
|
|
|
Integration Procedure ──→ Data Mapper (via DataRaptor Action element)
|
|
Integration Procedure ──→ Apex Class (via Remote Action element)
|
|
Integration Procedure ──→ HTTP Endpoint (via HTTP Action element)
|
|
Integration Procedure ──→ Integration Procedure (via nested IP Action element)
|
|
Integration Procedure ──→ OmniScript (via OmniScript Action element — uncommon)
|
|
|
|
FlexCard ──→ Integration Procedure (via data source configuration)
|
|
FlexCard ──→ Apex Class (via Apex data source)
|
|
FlexCard ──→ FlexCard (via child card reference)
|
|
FlexCard ──→ OmniScript (via action configuration — launches OS)
|
|
|
|
Data Mapper ──→ Salesforce Object (via InputObjectName — read)
|
|
Data Mapper ──→ Salesforce Object (via OutputObjectName — write)
|
|
```
|
|
|
|
---
|
|
|
|
## OmniScript Dependencies
|
|
|
|
### Element Types and Their Dependency Targets
|
|
|
|
OmniScript elements are stored as `OmniProcessElement` records (Core) or `Element__c` records (Vlocity). Each element has a `PropertySetConfig` / `PropertySet__c` JSON field containing the configuration.
|
|
|
|
#### DataRaptor Transform Action
|
|
|
|
Calls a Data Mapper to extract, transform, or load data.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "DataRaptor Transform Action",
|
|
"PropertySet": {
|
|
"bundle": "AccountExtract",
|
|
"bundleName": "AccountExtract",
|
|
"dataRaptorType": "Extract"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.bundle` or `PropertySet.bundleName` → resolves to an `OmniDataTransform` record by `Name`.
|
|
|
|
#### DataRaptor Turbo Action
|
|
|
|
High-performance variant of DataRaptor Transform Action. Same JSON structure, same extraction rule.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "DataRaptor Turbo Action",
|
|
"PropertySet": {
|
|
"bundle": "AccountTurboExtract",
|
|
"bundleName": "AccountTurboExtract"
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Integration Procedure Action
|
|
|
|
Calls an Integration Procedure.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "Integration Procedure Action",
|
|
"PropertySet": {
|
|
"integrationProcedureKey": "TypeName_SubTypeName",
|
|
"ipMethod": "TypeName",
|
|
"ipType": "SubTypeName",
|
|
"integrationProcedureVersion": 1
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.integrationProcedureKey` → resolves to an `OmniProcess` record where `Type_SubType` matches and `TypeCategory = 'IntegrationProcedure'`.
|
|
|
|
#### OmniScript Action (Embedded OmniScript)
|
|
|
|
Embeds or launches another OmniScript.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "OmniScript",
|
|
"PropertySet": {
|
|
"Type": "ChildScriptType",
|
|
"Sub Type": "ChildScriptSubType",
|
|
"Language": "English"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.Type` + `PropertySet["Sub Type"]` + `PropertySet.Language` → resolves to an `OmniProcess` where `TypeCategory = 'OmniScript'` and fields match.
|
|
|
|
#### Remote Action
|
|
|
|
Calls an Apex class method.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "Remote Action",
|
|
"PropertySet": {
|
|
"remoteClass": "MyApexClassName",
|
|
"remoteMethod": "myMethodName",
|
|
"remoteTimeout": 30000
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.remoteClass` → Apex class name. `PropertySet.remoteMethod` → method name. Dependency is `remoteClass.remoteMethod`.
|
|
|
|
#### HTTP Action
|
|
|
|
Calls an external HTTP endpoint.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "HTTP Action",
|
|
"PropertySet": {
|
|
"httpUrl": "{externalEndpointUrl}",
|
|
"httpMethod": "POST",
|
|
"namedCredential": "MyNamedCredential"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.httpUrl` or `PropertySet.namedCredential` → external dependency. If `namedCredential` is present, it references a Named Credential record.
|
|
|
|
#### Custom Lightning Web Component
|
|
|
|
Embeds an LWC inside the OmniScript.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "Custom Lightning Web Component",
|
|
"PropertySet": {
|
|
"lwcName": "myCustomComponent",
|
|
"lwcComponentName": "c-my-custom-component"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.lwcName` or `PropertySet.lwcComponentName` → LWC component reference.
|
|
|
|
#### DocuSign Envelope Action
|
|
|
|
Triggers a DocuSign envelope.
|
|
|
|
**PropertySetConfig structure**:
|
|
```json
|
|
{
|
|
"Type": "DocuSign Envelope Action",
|
|
"PropertySet": {
|
|
"docuSignTemplateId": "template-uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `PropertySet.docuSignTemplateId` → DocuSign template (external dependency).
|
|
|
|
---
|
|
|
|
## Integration Procedure Dependencies
|
|
|
|
Integration Procedures use the same element types as OmniScripts but are filtered by `TypeCategory = 'IntegrationProcedure'` (Core) or `IsIntegrationProcedure__c = true` (Vlocity).
|
|
|
|
### Element Types Available in IPs
|
|
|
|
| Element Type | Available in IP | Dependency Target |
|
|
|-------------|----------------|-------------------|
|
|
| DataRaptor Transform Action | Yes | Data Mapper |
|
|
| DataRaptor Turbo Action | Yes | Data Mapper |
|
|
| Remote Action | Yes | Apex Class |
|
|
| HTTP Action | Yes | External endpoint |
|
|
| Integration Procedure Action | Yes (nested) | Another IP |
|
|
| Matrix Action | Yes | Calculation Matrix |
|
|
| Set Values | Yes | None (internal) |
|
|
| Conditional Block | Yes | None (internal) |
|
|
| Loop Block | Yes | None (internal) |
|
|
| Response Action | Yes | None (internal) |
|
|
| List Action | Yes | None (internal) |
|
|
|
|
### Nested IP Pattern
|
|
|
|
An Integration Procedure can call another Integration Procedure, creating a chain:
|
|
|
|
```
|
|
IP: OrderValidation
|
|
└── IP Action → IP: CustomerLookup
|
|
└── DR Action → DM: CustomerExtract
|
|
└── IP Action → IP: InventoryCheck
|
|
└── HTTP Action → External inventory API
|
|
└── DR Action → DM: OrderTransform
|
|
```
|
|
|
|
---
|
|
|
|
## FlexCard Dependencies
|
|
|
|
FlexCards store their entire configuration in the `Definition` JSON field on `OmniUiCard` (Core) or `VlocityUITemplate__c` (Vlocity).
|
|
|
|
### Data Source Parsing
|
|
|
|
The `Definition` JSON contains a `dataSources` array:
|
|
|
|
```json
|
|
{
|
|
"dataSources": [
|
|
{
|
|
"name": "AccountData",
|
|
"type": "IntegrationProcedure",
|
|
"value": {
|
|
"key": "fetchAccountData",
|
|
"inputMap": { "AccountId": "{recordId}" }
|
|
}
|
|
},
|
|
{
|
|
"name": "DirectApex",
|
|
"type": "Apex",
|
|
"value": {
|
|
"className": "AccountSummaryController",
|
|
"methodName": "getSummary"
|
|
}
|
|
},
|
|
{
|
|
"name": "SObjectData",
|
|
"type": "SObject",
|
|
"value": {
|
|
"sObjectType": "Account",
|
|
"fields": ["Name", "Industry", "Phone"]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Extraction rules by data source type**:
|
|
|
|
| Data Source Type | JSON Path | Dependency Target |
|
|
|-----------------|-----------|-------------------|
|
|
| `IntegrationProcedure` | `value.key` | Integration Procedure (Type_SubType key) |
|
|
| `Apex` | `value.className` | Apex Class |
|
|
| `SObject` | `value.sObjectType` | Salesforce Object (direct SOQL) |
|
|
| `DataRaptor` | `value.bundle` | Data Mapper |
|
|
|
|
### Child Card References
|
|
|
|
FlexCards can embed other FlexCards:
|
|
|
|
```json
|
|
{
|
|
"children": [
|
|
{
|
|
"cardName": "ContactListCard",
|
|
"cardType": "childCard"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `children[].cardName` → resolves to another `OmniUiCard` by Name.
|
|
|
|
### Action References
|
|
|
|
FlexCard actions can launch OmniScripts:
|
|
|
|
```json
|
|
{
|
|
"actions": [
|
|
{
|
|
"actionType": "OmniScript",
|
|
"actionValue": {
|
|
"type": "editAccount",
|
|
"subType": "step1",
|
|
"language": "English"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
**Extraction rule**: `actions[].actionValue.type` + `subType` + `language` → resolves to an OmniScript.
|
|
|
|
---
|
|
|
|
## Data Mapper Dependencies
|
|
|
|
Data Mappers (DataRaptors) reference Salesforce objects through their items.
|
|
|
|
### Object References via Items
|
|
|
|
Each `OmniDataTransformItem` (Core) or `DRMapItem__c` (Vlocity) record contains:
|
|
|
|
| Field | Purpose | Dependency Type |
|
|
|-------|---------|----------------|
|
|
| `InputObjectName` / `InterfaceObject__c` | Source sObject for reads | sObject (read access) |
|
|
| `OutputObjectName` / `TargetFieldObjectType__c` | Target sObject for writes | sObject (write access) |
|
|
| `InputFieldName` / `InterfaceFieldAPIName__c` | Source field | Field-level dependency |
|
|
| `OutputFieldName` / `TargetFieldAPIName__c` | Target field | Field-level dependency |
|
|
|
|
### Extract Type Data Mapper Example
|
|
|
|
```
|
|
DM: AccountExtract (Type: Extract)
|
|
├── Item 1: InputObjectName = "Account"
|
|
│ InputFieldName = "Name"
|
|
│ OutputFieldName = "AccountName"
|
|
├── Item 2: InputObjectName = "Account"
|
|
│ InputFieldName = "Industry"
|
|
│ OutputFieldName = "AccountIndustry"
|
|
└── Item 3: InputObjectName = "Contact"
|
|
InputFieldName = "Email"
|
|
OutputFieldName = "PrimaryEmail"
|
|
|
|
Dependencies: Account (read), Contact (read)
|
|
```
|
|
|
|
### Load Type Data Mapper Example
|
|
|
|
```
|
|
DM: OrderCreate (Type: Load)
|
|
├── Item 1: OutputObjectName = "Order"
|
|
│ InputFieldName = "OrderData.accountId"
|
|
│ OutputFieldName = "AccountId"
|
|
└── Item 2: OutputObjectName = "OrderItem"
|
|
InputFieldName = "OrderData.lineItems[].productId"
|
|
OutputFieldName = "Product2Id"
|
|
|
|
Dependencies: Order (write), OrderItem (write)
|
|
```
|
|
|
|
### Transform Type Data Mapper
|
|
|
|
Transform type Data Mappers do not reference Salesforce objects directly — they map between data structures. They have no sObject dependencies but may participate in a chain:
|
|
|
|
```
|
|
OmniScript → DR Extract (reads Account) → DR Transform (reshapes data) → DR Load (writes CustomObj__c)
|
|
```
|
|
|
|
---
|
|
|
|
## Circular Dependency Detection
|
|
|
|
### Why Circular Dependencies Occur
|
|
|
|
Circular references happen when component A depends on component B, which directly or transitively depends back on component A. Common scenarios:
|
|
|
|
1. **OmniScript ↔ IP**: OmniScript calls IP via IP Action, IP calls back to OmniScript via OmniScript Action
|
|
2. **IP ↔ IP**: IP A calls IP B via nested IP Action, IP B calls IP A
|
|
3. **FlexCard → IP → OmniScript → FlexCard**: FlexCard sources data from IP, IP triggers OmniScript, OmniScript launches FlexCard
|
|
|
|
### Detection Algorithm
|
|
|
|
```
|
|
function detectCircularDependencies(graph):
|
|
cycles = []
|
|
for each node N in graph:
|
|
visited = empty set
|
|
path = empty list
|
|
dfs(N, visited, path, graph, cycles)
|
|
return cycles
|
|
|
|
function dfs(node, visited, path, graph, cycles):
|
|
if node is in path:
|
|
// Circular reference found
|
|
cycleStart = index of node in path
|
|
cycle = path[cycleStart:] + [node]
|
|
cycles.append(cycle)
|
|
return
|
|
if node is in visited:
|
|
return
|
|
visited.add(node)
|
|
path.append(node)
|
|
for each neighbor of node in graph:
|
|
dfs(neighbor, visited, path, graph, cycles)
|
|
path.removeLast()
|
|
```
|
|
|
|
### Reporting Circular References
|
|
|
|
When a cycle is detected, report it clearly:
|
|
|
|
```
|
|
CIRCULAR DEPENDENCY DETECTED:
|
|
OS:editAccount → IP:validateAccount → OS:editAccount
|
|
|
|
Components in cycle:
|
|
1. OmniScript "editAccount" (IP Action → validateAccount)
|
|
2. Integration Procedure "validateAccount" (OmniScript Action → editAccount)
|
|
|
|
Risk: Runtime infinite loop if not guarded by conditional logic.
|
|
Recommendation: Review whether the back-reference is intentional and has
|
|
a termination condition.
|
|
```
|
|
|
|
---
|
|
|
|
## Dependency Graph Construction
|
|
|
|
### Step-by-Step Process
|
|
|
|
```
|
|
1. DETECT namespace (see namespace-guide.md)
|
|
|
|
2. QUERY all container objects:
|
|
- OmniProcess (OmniScripts + IPs)
|
|
- OmniUiCard (FlexCards)
|
|
- OmniDataTransform (Data Mappers)
|
|
|
|
3. QUERY all element objects:
|
|
- OmniProcessElement (for each OmniProcess)
|
|
- OmniDataTransformItem (for each OmniDataTransform)
|
|
|
|
4. PARSE each element's PropertySetConfig:
|
|
- Identify element Type
|
|
- Extract dependency reference per extraction rules above
|
|
- Resolve reference to a known component record
|
|
|
|
5. PARSE each FlexCard's Definition:
|
|
- Extract dataSources array
|
|
- Extract children array
|
|
- Extract actions array
|
|
- Resolve references to known components
|
|
|
|
6. PARSE each Data Mapper's items:
|
|
- Extract InputObjectName / OutputObjectName
|
|
- Resolve to sObject names
|
|
|
|
7. BUILD directed graph:
|
|
- Nodes = all components + referenced sObjects + external endpoints
|
|
- Edges = dependency references with type labels
|
|
|
|
8. DETECT circular references:
|
|
- Run DFS cycle detection
|
|
- Record all cycles found
|
|
|
|
9. COMPUTE impact analysis:
|
|
- For each node, compute transitive closure of inbound edges
|
|
- "If X changes, these components are affected"
|
|
```
|
|
|
|
### Impact Analysis: Reverse Dependency Lookup
|
|
|
|
To answer "what breaks if I change Data Mapper X?", reverse the dependency direction:
|
|
|
|
```
|
|
Given: DM:AccountExtract
|
|
|
|
Direct dependents (components that reference this DM):
|
|
→ IP:fetchAccountData (DataRaptor Action)
|
|
|
|
Transitive dependents (components that reference the direct dependents):
|
|
→ OS:updateAccount (IP Action → fetchAccountData)
|
|
→ FC:AccountSummaryCard (Data Source → fetchAccountData)
|
|
|
|
Full impact set: [IP:fetchAccountData, OS:updateAccount, FC:AccountSummaryCard]
|
|
```
|
|
|
|
---
|
|
|
|
## Property Set Config Parsing Tips
|
|
|
|
### Handling Large JSON
|
|
|
|
`PropertySetConfig` can exceed 100KB for complex elements. When querying via SOQL:
|
|
- SOQL `SELECT` returns the full field value
|
|
- For very large configs, the Tooling API may be necessary
|
|
- Parse incrementally if memory is a concern
|
|
|
|
### Nested Property Sets
|
|
|
|
Some elements have nested structures. Always check for:
|
|
- `PropertySet.bundle` (top-level reference)
|
|
- `PropertySet.elementProperties` (per-field configs)
|
|
- `PropertySet.conditionalProperties` (conditional logic)
|
|
- `PropertySet.remoteOptions` (additional remote action config)
|
|
|
|
### Common Pitfalls
|
|
|
|
| Pitfall | Handling |
|
|
|---------|---------|
|
|
| `bundleName` vs `bundle` | Both may exist; prefer `bundle` as the canonical reference |
|
|
| `integrationProcedureKey` format | Always `Type_SubType` with underscore separator |
|
|
| Version-specific references | Some elements reference a specific version; default is latest active |
|
|
| Null PropertySetConfig | Skip elements with null/empty config — they have no dependencies |
|
|
| JSON parsing errors | Malformed JSON in PropertySetConfig can occur on manually edited records; catch and log |
|