diff --git a/CHANGELOG.md b/CHANGELOG.md index 3417fcb..51e49e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **Hyphenated saved-query param names** — the workflow template + resolver now accepts hyphens in identifiers, so references like + `${{ params.vendor-no }}` substitute correctly. Previously the regex + matched only `[\w.]`, silently leaving the literal `${{ … }}` token + in the rendered filter (BC then 400'd or, worse, returned mismatched + rows). Affects both `bcli q` saved queries and `bcli batch` + workflows. + ## [0.2.0] — 2026-05-06 ### Added diff --git a/src/bcli/workflow/_resolver.py b/src/bcli/workflow/_resolver.py index 1d87cd2..cc16e83 100644 --- a/src/bcli/workflow/_resolver.py +++ b/src/bcli/workflow/_resolver.py @@ -9,11 +9,13 @@ from bcli.workflow._models import WorkflowContext # Matches ${{ steps.name.path }} or ${{ params.key }} -REFERENCE_PATTERN = re.compile(r"\$\{\{\s*(steps|params)\.([\w.]+)\s*\}\}") +# Identifier set: word chars + dot (for nested step output paths) + hyphen +# (param names like ``vendor-no`` are commonly used by saved queries). +REFERENCE_PATTERN = re.compile(r"\$\{\{\s*(steps|params)\.([\w.\-]+)\s*\}\}") # Same pattern, but must be the *entire* string (for type preservation). FULL_REFERENCE_PATTERN = re.compile( - r"^\s*\$\{\{\s*(steps|params)\.([\w.]+)\s*\}\}\s*$" + r"^\s*\$\{\{\s*(steps|params)\.([\w.\-]+)\s*\}\}\s*$" ) diff --git a/tests/test_workflow/test_resolver.py b/tests/test_workflow/test_resolver.py index d012665..cebbce0 100644 --- a/tests/test_workflow/test_resolver.py +++ b/tests/test_workflow/test_resolver.py @@ -55,6 +55,16 @@ def test_bool_param_type_preserved(self): ctx = _ctx(params={"active": True}) assert resolve_references("${{ params.active }}", ctx) is True + def test_hyphenated_param_name(self): + ctx = _ctx(params={"vendor-no": "V00011", "ship-no": "SHIP0045"}) + # Embedded reference with hyphenated key + assert ( + resolve_references("vendor eq '${{ params.vendor-no }}'", ctx) + == "vendor eq 'V00011'" + ) + # Full-string reference with hyphenated key (type preservation path) + assert resolve_references("${{ params.ship-no }}", ctx) == "SHIP0045" + def test_mixed_string_interpolation(self): ctx = _ctx(params={"vendor_no": "V00011"}) result = resolve_references("Invoice for ${{ params.vendor_no }}", ctx)