afv-library/skills/omnistudio-dependencies-analyze/references/dependency-patterns.md

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 |