-
Notifications
You must be signed in to change notification settings - Fork 850
fix: Support String Interpolation for Variables and Resources #7255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix: Support String Interpolation for Variables and Resources #7255
Conversation
…d `$encrypted:` references within string values. Previously, these references were only replaced when they appeared as the entire value. Now they can be embedded within strings, enabling use cases like `"Bearer $var:api_token"` or `"https://api.example.com?key=$var:api_key"`. When using generated UI forms or HTTP request nodes, users often need to compose strings that include variable values. For example: ```javascript // Input in generated UI: var1: "$var:f/Narration/jina_api_key" api_key: "Bearer $var:f/Narration/jina_api_key" ``` Previously, the behavior was: - ✅ `var1` → Replaced with actual value: `"jina_4797cd5bb8f54314"` - ❌ `api_key` → **NOT replaced**, remained as literal string: `"Bearer $var:f/Narration/jina_api_key"` This limitation forced users to handle string concatenation in their scripts, which is problematic for generic HTTP request nodes where the code doesn't know which parameters need concatenation. This PR implements string interpolation for variable/resource/encrypted references: ```rust Value::String(y) if y.starts_with("$var:") => { // Only replaces if entire string is "$var:path" } ``` ```rust Value::String(y) if y.starts_with("$var:") && !y.contains(' ') => { // Exact match: entire string is a variable reference // Returns the variable value (could be any JSON type) } Value::String(y) if (*RE_RES_VAR).is_match(&y) => { // String interpolation: scan and replace all occurrences // Always returns a string with substitutions made } ``` The regex pattern `\$var:([^\s"'\},\)\]&;]+)` extracts variable paths, stopping at common delimiters: - Whitespace, quotes (`"`, `'`) - JSON/array terminators (`}`, `,`, `)`, `]`) - URL/query separators (`&`, `;`) This ensures proper extraction in contexts like: - JSON values: `{"Authorization": "$var:token"}` - URL parameters: `?key=$var:api_key&other=value` - Arrays: `["$var:item1", "$var:item2"]` - Function calls: `func($var:arg)` 1. **Added regex patterns** for extracting variable/resource/encrypted references: ```rust static ref RE_VAR_PATTERN: Regex = Regex::new(r#"\$var:([^\s"'\},\)\]&;]+)"#).unwrap(); static ref RE_RES_PATTERN: Regex = Regex::new(r#"\$res:([^\s"'\},\)\]&;]+)"#).unwrap(); static ref RE_ENCRYPTED_PATTERN: Regex = Regex::new(r#"\$encrypted:([^\s"'\},\)\]&;]+)"#).unwrap(); ``` 2. **Modified `transform_json_value()`** to handle both exact matches and string interpolation: - Exact match (e.g., `"$var:path"`) → Returns variable value as-is (preserves type) - String interpolation (e.g., `"Bearer $var:token"`) → Scans string, replaces all matches, returns string Applied the same changes to the API layer's `transform_json_value()` function to ensure consistency across all variable resolution paths. ```javascript // Input: "$var:f/config/timeout" // Variable value: 30 (number) // Output: 30 (preserves type) ``` ```javascript // Input: "Bearer $var:f/auth/api_token" // Variable value: "sk-abc123" // Output: "Bearer sk-abc123" (string) ``` ```javascript // Input: "$var:greeting $var:name" // Variables: greeting="Hello", name="World" // Output: "Hello World" ``` ```javascript // Input: "https://api.example.com?key=$var:api_key&user=$res:f/config/user_id" // Output: "https://api.example.com?key=abc123&user=42" ``` - If a referenced variable/resource doesn't exist, the job fails with a clear error message: ``` Variable f/auth/api_token not found in string interpolation for `Authorization`: ... ``` - Path validation is maintained (e.g., resources must have at least 2 path segments) Tested with various scenarios: - ✅ Single variable in string - ✅ Multiple variables in same string - ✅ Mixed `$var:` and `$res:` references - ✅ JSON contexts with quotes, brackets, braces - ✅ URL query parameters with `&` separators - ✅ Array/function contexts - ✅ Whitespace and special character boundaries - ✅ Non-existent variable error handling ✅ **Fully backward compatible** - Exact match behavior unchanged: `"$var:path"` still returns the variable value with original type - Existing scripts and workflows continue to work without modification - Only adds new functionality for strings containing embedded references ❌ **No frontend changes required** All variable substitution happens on the backend. Frontend components that display or edit `$var:` references remain unchanged. Fixes the issue where users couldn't use variable references within composed strings, particularly problematic for: - Generic HTTP request nodes - Authorization headers - API URL construction - Configuration templates --- - `backend/windmill-worker/src/common.rs` - Worker-side variable transformation - `backend/windmill-api/src/resources.rs` - API-side variable transformation
|
I have read the CLA Document and I hereby sign the CLA rejectliu seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. |
|
|
||
| match client.get_variable_value(path).await { | ||
| Ok(value) => { | ||
| result = result.replace(full_match, &value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the interpolation branch for string values (starting at line 287), the loops over RE_VAR_PATTERN and RE_RES_PATTERN use .replace() in each iteration. If a variable appears multiple times, this could trigger redundant lookups. Consider caching the replacement values to improve efficiency.
|
This issue found when i am trying to migrate my script from N8N, to align function with n8n |
|
Thank you for the contribution. I'm not convinced this is something we should allow. It shifts the complexity from being in the data structure/schema to being in the interpolation which is arguably worse. In your example, api key should be one of the field and the logic of the script prepend it with |
but if user need a common http request node support header set from ui like n8n does, that will be necessary |
|
@androidfans would you have the code of the script you have in mind? |
interface Header {
key: string;
value: string;
}
export async function main(
url: string,
request_type: string,
headers: Header[] = [],
body: string = ""
) {
const headersInstance = new Headers();
headers.forEach(header => {
if (header.key && header.value) {
headersInstance.append(header.key, header.value);
}
});
const method = request_type.toUpperCase();
const hasBody = method !== 'GET' && method !== 'HEAD';
const requestBody = (hasBody && body) ? body : undefined;
const req = new Request(url, {
method: method,
headers: headersInstance,
body: requestBody
});
try {
const resp = await fetch(req);
const responseText = await resp.text();
let responseData: any = responseText;
if (responseText) {
try {
responseData = JSON.parse(responseText);
} catch (e) {
}
}
return {
response: responseData,
status: resp.status,
statusText: resp.statusText,
ok: resp.ok
};
} catch (error) {
throw new Error(`Request failed: ${error instanceof Error ? error.message : String(error)}`);
}
}you can see the network node from n8n, support add header , and header string support insert value.
some main blocker blocks me migrate my work flow from n8n to windmill is
|

Support String Interpolation for Variables and Resources
Summary
This PR adds support for string interpolation of
$var:,$res:, and$encrypted:references within string values. Previously, these references were only replaced when they appeared as the entire value. Now they can be embedded within strings, enabling use cases like"Bearer $var:api_token"or"https://api.example.com?key=$var:api_key".Problem Statement
When using generated UI forms or HTTP request nodes, users often need to compose strings that include variable values. For example:
Previously, the behavior was:
var1→ Replaced with actual value:"jina_4797cd5bb8f54314"api_key→ NOT replaced, remained as literal string:"Bearer $var:f/Narration/jina_api_key"This limitation forced users to handle string concatenation in their scripts, which is problematic for generic HTTP request nodes where the code doesn't know which parameters need concatenation.
Solution
This PR implements string interpolation for variable/resource/encrypted references:
Before (Exact Match Only)
After (String Interpolation)
Interpolation Pattern
The regex pattern
\$var:([^\s"'\},\)\]&;]+)extracts variable paths, stopping at common delimiters:",')},,,),])&,;)This ensures proper extraction in contexts like:
{"Authorization": "$var:token"}?key=$var:api_key&other=value["$var:item1", "$var:item2"]func($var:arg)Changes
Backend Worker (
windmill-worker/src/common.rs)transform_json_value()to handle both exact matches and string interpolation:"$var:path") → Returns variable value as-is (preserves type)"Bearer $var:token") → Scans string, replaces all matches, returns stringAPI Layer (
windmill-api/src/resources.rs)Applied the same changes to the API layer's
transform_json_value()function to ensure consistency across all variable resolution paths.Behavior
Exact Match (Unchanged)
String Interpolation (New)
Multiple Substitutions (New)
Mixed Types (New)
Error Handling
Testing
Tested with various scenarios:
$var:and$res:references&separatorsBackward Compatibility
✅ Fully backward compatible
"$var:path"still returns the variable value with original typeFrontend Changes
❌ No frontend changes required
All variable substitution happens on the backend. Frontend components that display or edit
$var:references remain unchanged.Related Issues
#7253
Fixes the issue where users couldn't use variable references within composed strings, particularly problematic for: