|
1 | 1 | --- |
2 | 2 | sidebar_position: 3 |
3 | 3 | title: Expressions Syntax |
4 | | -description: Learn how to use expressions in FlowSynx workflows to reference outputs, variables, and nested data dynamically during workflow execution. |
| 4 | +description: FlowSynx expression system for dynamic access to variables, task outputs, nested data, arithmetic, conditionals, boolean logic, and functional aggregation. |
5 | 5 | --- |
6 | 6 |
|
7 | 7 | # FlowSynx Expression Syntax |
8 | 8 |
|
9 | | -FlowSynx supports a flexible **expression system** that allows you to reference **workflow outputs**, **variables**, and **nested properties** dynamically within workflow definitions. |
10 | | -Expressions are resolved at runtime by the `ExpressionParser` engine. |
| 9 | +FlowSynx provides a powerful expression system for dynamically resolving **workflow variables**, **task outputs**, **nested properties**, **arithmetic**, **boolean logic**, **conditional (ternary) operations**, and **functional aggregations** at runtime. |
| 10 | +All expressions are evaluated by the `ExpressionParser`. |
11 | 11 |
|
12 | | -## Overview |
13 | | - |
14 | | -Expressions are enclosed within the syntax: |
| 12 | +## Core Delimiter |
15 | 13 |
|
| 14 | +Wrap any resolvable content in: |
16 | 15 | ``` |
17 | 16 | $[...] |
18 | 17 | ``` |
19 | 18 |
|
20 | | -Anything inside `$[...]` is evaluated by the expression parser. |
| 19 | +If the entire value is a single `$[...]` block, the resolved object is returned directly (e.g. an array or JSON object). If embedded inside a larger string, the resolved value is converted to a string and concatenated. |
| 20 | + |
| 21 | +## Data Sources |
| 22 | + |
| 23 | +| Source | Syntax | Description | |
| 24 | +| ------ | ------ | ----------- | |
| 25 | +| Variables | `$[Variables('Key')]` | Workflow-level variable lookup (case-insensitive). | |
| 26 | +| Outputs | `$[Outputs('TaskName')]` | Output of a previously executed task (case-insensitive). | |
21 | 27 |
|
22 | | -You can use this syntax anywhere in your workflow — for example in **task parameters**, **paths**, or **input data** fields. |
| 28 | +Keys with spaces or special characters should use single quotes: `$[Variables('My Custom Key')]`. |
23 | 29 |
|
24 | | -## Expression Sources |
| 30 | +### Outputs as PluginContext |
25 | 31 |
|
26 | | -FlowSynx supports two main data sources inside expressions: |
| 32 | +The [`Outputs`](command:_github.copilot.openSymbolFromReferences?%5B%22%22%2C%5B%7B%22uri%22%3A%7B%22scheme%22%3A%22file%22%2C%22authority%22%3A%22%22%2C%22path%22%3A%22%2Fc%3A%2Fgithub%2Fflowsynx%2Fdocs%2Fdocs%2Freference%2Fflowsynx%2Fexpressions.mdx%22%2C%22query%22%3A%22%22%2C%22fragment%22%3A%22%22%7D%2C%22pos%22%3A%7B%22line%22%3A25%2C%22character%22%3A2%7D%7D%5D%2C%2203b219c3-6667-4007-919c-1aa2e2101390%22%5D "Go to definition") object provides access to the following properties: |
27 | 33 |
|
28 | | -| Source | Syntax Example | Description | |
29 | | -| ------- | --------------- | ----------- | |
30 | | -| **Variables** | `$[Variables('InputPath')]` | References workflow-level variables defined under `"Variables"` in the workflow JSON. | |
31 | | -| **Outputs** | `$[Outputs('TaskName')]` | References the output of a previously executed task. | |
| 34 | +| Property | Description | |
| 35 | +| ---------------- | --------------------------------------------------------------------------- | |
| 36 | +| `Id` | The unique identifier of the plugin context. | |
| 37 | +| `SourceType` | The type of source that generated the output. | |
| 38 | +| `Format` | The format of the output, if applicable. | |
| 39 | +| `Metadata` | A dictionary containing metadata as key-value pairs. | |
| 40 | +| `RawData` | The raw binary data of the output, if available. | |
| 41 | +| `Content` | The string content of the output, if available. | |
| 42 | +| `StructuredData` | A list of dictionaries representing structured data, if applicable. | |
32 | 43 |
|
33 | | -Both can be used in nested or concatenated contexts. |
| 44 | +Keys with spaces or special characters should use single quotes: `$[Variables('My Custom Key')]`. |
34 | 45 |
|
35 | | -## Basic Examples |
| 46 | +#### Example |
36 | 47 |
|
37 | | -### Accessing Workflow Variables |
| 48 | +##### Accessing Metadata |
| 49 | +You can retrieve specific metadata values using dot notation: |
38 | 50 |
|
39 | | -```jsonc |
40 | | -"Parameters": { |
41 | | - "Path": "$[Variables('InputPath')]" |
42 | | -} |
| 51 | +``` |
| 52 | +$[Outputs('TaskName').Metadata['Key']] |
43 | 53 | ``` |
44 | 54 |
|
45 | | -If your workflow defines: |
| 55 | +##### Accessing Content |
| 56 | +To retrieve the content of the output: |
46 | 57 |
|
47 | | -```jsonc |
48 | | -"Variables": { |
49 | | - "InputPath": "s3://bucket/input" |
50 | | -} |
| 58 | +``` |
| 59 | +$[Outputs('TaskName').Content] |
51 | 60 | ``` |
52 | 61 |
|
53 | | -The parser will replace `$[Variables('InputPath')]` with: |
| 62 | +##### Accessing Structured Data |
| 63 | +You can traverse the StructuredData property to access specific elements: |
54 | 64 |
|
55 | 65 | ``` |
56 | | -s3://bucket/input |
| 66 | +$[Outputs('TaskName').StructuredData[0]['FieldName']] |
57 | 67 | ``` |
58 | 68 |
|
59 | | -### Accessing Task Outputs |
| 69 | +##### Combining Properties |
| 70 | +You can combine multiple properties in expressions: |
60 | 71 |
|
61 | | -```jsonc |
62 | | -"Parameters": { |
63 | | - "Data": "$[Outputs('ExtractData')]" |
64 | | -} |
| 72 | +``` |
| 73 | +$[Outputs('TaskName').Id + ' - ' + Outputs('TaskName').SourceType] |
| 74 | +``` |
| 75 | + |
| 76 | +#### Notes on PluginContext |
| 77 | +- Metadata keys are case-insensitive. |
| 78 | +- RawData is a binary array and cannot be directly used in string concatenation. You may need to convert it to a string or process it separately. |
| 79 | +- StructuredData is a list of dictionaries, allowing traversal and dynamic access to nested fields. |
| 80 | + |
| 81 | +#### Updated Examples |
| 82 | +Here are some updated examples reflecting the PluginContext structure: |
| 83 | + |
| 84 | +``` |
| 85 | +$[Outputs('Task').Id] |
| 86 | +$[Outputs('Task').Metadata['Category']] |
| 87 | +$[Outputs('Task').StructuredData[0]['Name']] |
| 88 | +$[Contains(Outputs('Task').Metadata, 'urgent')] |
| 89 | +$[Outputs('Task').Content == 'success' ? 'completed' : 'retry'] |
65 | 90 | ``` |
66 | 91 |
|
67 | | -This retrieves the **entire output** of a task named `ExtractData`. |
| 92 | +#### Error Handling for PluginContext |
| 93 | +- Accessing a non-existent property or key in Metadata or StructuredData will throw a FlowSynxException with the code ExpressionParserKeyNotFound. |
| 94 | +- Ensure that RawData and StructuredData are not null before accessing their contents. |
68 | 95 |
|
69 | | -## Nested Property Access |
| 96 | +This update ensures that the Outputs object is documented accurately as a PluginContext type, reflecting its properties and usage in expressions. |
70 | 97 |
|
71 | | -You can access **nested fields** or **array elements** inside objects and arrays using `.` (dot notation) or `[]` (indexers): |
| 98 | +## Nested Access |
| 99 | + |
| 100 | +Use dot notation and indexers: |
72 | 101 |
|
73 | 102 | | Example | Meaning | |
74 | | -| -------- | -------- | |
75 | | -| `$[Outputs('ExtractData').Records[0].Id]` | Accesses the `Id` property of the first record in the `Records` array. | |
76 | | -| `$[Outputs('Transform').Result.Data.Value]` | Accesses a deeply nested property. | |
| 103 | +| ------- | ------- | |
| 104 | +| `$[Outputs('Extract').Id]` | Record's `Id`. | |
| 105 | +| `$[Outputs('Transform').Content]` | Property traversal. | |
| 106 | +| `$[Outputs('Task').Metadata[Variables('Index')]]` | Dynamic index. | |
77 | 107 |
|
78 | | -These can be combined with variable lookups: |
| 108 | +Arrays are supported for `IList` and `JArray`. Properties are resolved case-insensitively (`JObject`, `JToken`, or CLR objects). |
79 | 109 |
|
80 | | -```jsonc |
81 | | -"Parameters": { |
82 | | - "Path": "$[Variables('BasePath')]/$[Outputs('Task1').FileName]" |
83 | | -} |
84 | | -``` |
| 110 | +## Literal Values |
85 | 111 |
|
86 | | -If: |
87 | | -```jsonc |
88 | | -"Variables": { "BasePath": "/data/output" }, |
89 | | -"Outputs": { "Task1": { "FileName": "report.csv" } } |
90 | | -``` |
| 112 | +Inside expressions you can use: |
| 113 | +- Quoted strings: `'text'` or `"text"` |
| 114 | +- Numbers: `42`, `3.14` |
| 115 | +- Booleans: `true`, `false` |
| 116 | +- Null: `null` |
| 117 | + |
| 118 | +Example: `$['static-value']` |
| 119 | + |
| 120 | +## Functional Methods |
| 121 | + |
| 122 | +Built-in case-insensitive aggregation and search functions: |
| 123 | + |
| 124 | +| Function | Signature | Behavior | |
| 125 | +| -------- | --------- | -------- | |
| 126 | +| `Min` | `Min(arg1, arg2, collection, ...)` | Returns smallest numeric value (flattens enumerables). | |
| 127 | +| `Max` | `Max(...)` | Returns largest numeric value. | |
| 128 | +| `Sum` | `Sum(...)` | Sums all numeric values. | |
| 129 | +| `Avg` | `Avg(...)` | Arithmetic mean (0 if no numbers). | |
| 130 | +| `Count` | `Count(collection)` or `Count(arg1,arg2,...)` | If single enumerable → element count; otherwise number of arguments. | |
| 131 | +| `Contains` | `Contains(container, value)` | Case-insensitive search (strings, enumerables, or direct value comparison). | |
| 132 | + |
| 133 | +Arguments can be: |
| 134 | +- Literals |
| 135 | +- Variable/output references |
| 136 | +- Nested expressions |
| 137 | +- Other functional calls |
| 138 | + |
| 139 | +Examples: |
91 | 140 |
|
92 | | -Result: |
93 | 141 | ``` |
94 | | -/data/output/report.csv |
| 142 | +$[Sum(Variables('Prep').Values)] |
| 143 | +$[Avg(Variables('A').Numbers, Outputs('B').Content, 10)] |
| 144 | +$[Count(Variables('ListTask').Items)] |
| 145 | +$[Contains(Outputs('Meta').Metadata, 'critical')] |
| 146 | +$[Min(Variables('A').Values, Variables('B').Values)] |
95 | 147 | ``` |
96 | 148 |
|
97 | | -## Expression Evaluation Rules |
| 149 | +## Arithmetic Expressions |
98 | 150 |
|
99 | | -The `ExpressionParser` engine follows these rules: |
| 151 | +You can perform arithmetic inside `$[...]` using operators: `+ - * / %` and parentheses. |
100 | 152 |
|
101 | | -1. **Single Expression** |
102 | | - - If the expression is *entirely* a `$[...]` block, the value is returned directly as an object (not just a string). |
103 | | - - Example: `$[Outputs('Task1')]` returns the full object produced by `Task1`. |
| 153 | +``` |
| 154 | +$[(Variables('Calc').Base + Variables('Offset')) * 0.5] |
| 155 | +``` |
| 156 | + |
| 157 | +Resolution flow: |
| 158 | +1. Embedded `$[...]` segments inside the arithmetic are first resolved. |
| 159 | +2. `Variables(...)` / `Outputs(...)` inside arithmetic are replaced with their string value (missing values become `0` in arithmetic context). |
| 160 | +3. Expression is evaluated; numeric result returned as `double`. |
| 161 | +4. If evaluation fails, it falls back to literal/lookup interpretation. |
104 | 162 |
|
105 | | -2. **String Concatenation** |
106 | | - - If the expression is embedded within a larger string, the resolved value is converted to a string and concatenated. |
107 | | - - Example: `"File: $[Outputs('Task1').FileName]"` → `"File: result.csv"`. |
| 163 | +## Boolean Logic & Comparisons |
108 | 164 |
|
109 | | -3. **Nested Expressions** |
110 | | - - Expressions can reference other expressions recursively. |
111 | | - - Example: `$[Variables(Outputs('MetaTask').VariableKey)]`. |
| 165 | +Supported operators: `== != > < >= <= && || !` |
112 | 166 |
|
113 | | -4. **Case Insensitivity** |
114 | | - - Keys and property names are resolved case-insensitively (`Outputs('task1')` = `Outputs('Task1')`). |
| 167 | +Examples: |
115 | 168 |
|
116 | | -5. **Unbalanced Brackets or Parentheses** |
117 | | - - The parser detects and throws descriptive errors for unbalanced `[]` or `()` characters. |
| 169 | +``` |
| 170 | +$[Variables('Mode') == 'fast'] |
| 171 | +$[Variables('Stats').Errors > 0 && Variables('Stats').Warnings == 0] |
| 172 | +$[!Contains(Variables('Tags').All,'deprecated')] |
| 173 | +``` |
118 | 174 |
|
119 | | -6. **Quoted Keys** |
120 | | - - You can wrap keys in single quotes to include spaces or special characters: |
121 | | - `$[Variables('My Custom Key')]`. |
| 175 | +Comparison semantics: |
| 176 | +- Equality is case-insensitive string comparison for `==` / `!=`. |
| 177 | +- Relational operators (`> < >= <=`) apply only if both sides convert to numbers. |
122 | 178 |
|
123 | | -## Supported Operations |
| 179 | +## Conditional (Ternary) Expressions |
124 | 180 |
|
125 | | -### 1. Variable Access |
126 | 181 | ``` |
127 | | -$[Variables('key')] |
| 182 | +$[Variables('Score').Value >= 90 ? 'excellent' : 'standard'] |
| 183 | +$[Contains(Outputs('Meta').Metadata, 'urgent') ? Variables('UrgentPath') : Variables('NormalPath')] |
128 | 184 | ``` |
129 | 185 |
|
130 | | -### 2. Output Access |
| 186 | +Format: `condition ? whenTrue : whenFalse` |
| 187 | +Condition can include logical or comparison operators. |
| 188 | + |
| 189 | +## Recursive & Dynamic Keys |
| 190 | + |
| 191 | +You can build dynamic keys or nested expressions: |
| 192 | + |
131 | 193 | ``` |
132 | | -$[Outputs('taskName')] |
| 194 | +$[Variables(Outputs('Meta').Id)] |
| 195 | +$[Outputs(Variables('DynamicTask')).StructuredData] |
133 | 196 | ``` |
134 | 197 |
|
135 | | -### 3. Nested Field Access |
| 198 | +Resolution is recursive: inner expressions resolve before outer structures. |
| 199 | + |
| 200 | +## String Concatenation Example |
| 201 | + |
136 | 202 | ``` |
137 | | -$[Outputs('task').data.results[1].value] |
| 203 | +"Path": "$[Variables('Base')]/$[Outputs('Export').Id]" |
138 | 204 | ``` |
139 | 205 |
|
140 | | -### 4. Combined or Concatenated Strings |
| 206 | +If: |
| 207 | + |
141 | 208 | ``` |
142 | | -"Path": "logs/$[Variables('RunId')]/$[Outputs('Extract').FileName]" |
| 209 | +Variables: { "Base": "/data/out" } Outputs: { "Export": { "Id": "report.csv" } } |
143 | 210 | ``` |
144 | 211 |
|
145 | | -### 5. Recursive Expressions |
| 212 | +Result: |
| 213 | + |
146 | 214 | ``` |
147 | | -$[Variables(Outputs('Metadata').DynamicKey)] |
| 215 | +/data/out/report.csv |
148 | 216 | ``` |
149 | 217 |
|
150 | | -## Error Handling |
| 218 | +## Return Value Behavior |
151 | 219 |
|
152 | | -When an expression cannot be resolved, the parser throws a `FlowSynxException` with specific error codes. |
| 220 | +1. Whole expression only: `$[Outputs('Task')]` → returns the raw object (array, complex type, etc.). |
| 221 | +2. Embedded in text: `"File=$[Outputs('Task').Id]"` → string interpolation. |
| 222 | +3. Functions/Arithmetic: return numeric or boolean values directly if standalone. |
153 | 223 |
|
154 | | -| Error Code | Condition | Example | |
155 | | -| ----------- | ---------- | -------- | |
156 | | -| `ExpressionParserKeyNotFound` | Key or source (`Variables` / `Outputs`) is missing or invalid. | `$[Outputs('UnknownTask')]` | |
157 | | -| — | Unbalanced brackets or parentheses. | `$[Variables('Key'` | |
| 224 | +## Error Handling |
158 | 225 |
|
159 | | -## Example Use Case |
| 226 | +Parser throws `FlowSynxException` (code: `ExpressionParserKeyNotFound`) for: |
| 227 | +- Missing `Variables('key')` or `Outputs('key')`. |
| 228 | +- Unbalanced brackets `[...]` or parentheses `(...)`. |
| 229 | +- Invalid functional syntax (e.g. `Contains()` with argument count ≠ 2). |
| 230 | +- Malformed conditional expressions. |
160 | 231 |
|
161 | | -Here’s a real-world usage in a FlowSynx workflow task: |
| 232 | +In arithmetic-only contexts, missing lookups are coerced to `0` (to avoid failure). Direct lookup outside arithmetic still throws. |
162 | 233 |
|
163 | | -```jsonc |
164 | | -{ |
165 | | - "Name": "SaveData", |
166 | | - "Type": "FlowSynx.FileSystem", |
167 | | - "Dependencies": ["ToCSV"], |
168 | | - "Parameters": { |
169 | | - "Operation": "write", |
170 | | - "Path": "$[Variables('OutputDirectory')]/$[Outputs('ToCSV').FileName]", |
171 | | - "Data": "$[Outputs('ToCSV').Content]", |
172 | | - "Overwrite": true |
173 | | - } |
174 | | -} |
175 | | -``` |
| 234 | +## Examples |
176 | 235 |
|
177 | | -If: |
178 | | -```jsonc |
179 | | -"Variables": { "OutputDirectory": "/mnt/results" }, |
180 | | -"Outputs": { "ToCSV": { "FileName": "report.csv", "Content": "data" } } |
181 | 236 | ``` |
182 | | - |
183 | | -Then the evaluated parameters become: |
184 | | -```jsonc |
185 | | -{ |
186 | | - "Operation": "write", |
187 | | - "Path": "/mnt/results/report.csv", |
188 | | - "Data": "data", |
189 | | - "Overwrite": true |
190 | | -} |
| 237 | +$[Sum(Outputs('Collect').Content, 10, Variables('Bonus'))] |
| 238 | +$[Outputs('Metrics').Metadata[0] > 50 ? 'pass' : 'fail'] |
| 239 | +$[Contains(Outputs('Tags').List, Variables('TargetTag')) && Variables('Stats').Count >= 5] |
| 240 | +$[Min(Outputs('A').Content, Outputs('B').Content)] |
| 241 | +$[Variables(Outputs('Resolver').Id)] |
191 | 242 | ``` |
192 | 243 |
|
| 244 | + |
| 245 | +## Best Practices |
| 246 | + |
| 247 | +- Use single quotes for keys with spaces: `$[Variables('My Key')]`. |
| 248 | +- Keep arithmetic numeric; non-numeric tokens cause literal fallback. |
| 249 | +- Prefer explicit parentheses for complex logic. |
| 250 | +- Avoid over-nesting; consider storing intermediate values in variables. |
| 251 | + |
193 | 252 | ## Summary |
194 | 253 |
|
195 | | -FlowSynx Expressions allow: |
196 | | -- Dynamic parameter substitution. |
197 | | -- Flexible referencing of workflow data. |
198 | | -- Safe and validated parsing with descriptive errors. |
199 | | -- Recursive resolution for advanced workflow logic. |
| 254 | +FlowSynx Expressions support: |
| 255 | +- Dynamic access to variables and task outputs |
| 256 | +- Nested property and array traversal |
| 257 | +- Functional aggregation (Min/Max/Sum/Avg/Count/Contains) |
| 258 | +- Arithmetic and numeric evaluation |
| 259 | +- Boolean logic and conditional branching |
| 260 | +- Recursive and dynamic key resolution |
| 261 | +- Strong error signaling for invalid syntax |
200 | 262 |
|
201 | 263 | :::info |
202 | | -You can use `$[Variables(...)]` and `$[Outputs(...)]` in *any* string field inside a FlowSynx workflow — the parser will automatically handle context-aware replacements. |
| 264 | +Expressions can be used in any string field in workflow definitions. Mix multiple `$[...]` segments freely; each is resolved independently and merged into the final string. |
203 | 265 | ::: |
0 commit comments