feat(orchestrator-form-widgets): add ActiveBoolean widget for dynamic boolean form fields - #4440
Conversation
… boolean form fields Add ActiveBoolean widget that enables boolean fields in Orchestrator workflow forms to dynamically fetch values from external APIs using the same ui:props pattern as existing Active widgets. Features: - Supports fetch:url, fetch:response:value, fetch:response:default - Coerces string and number values to boolean - Supports fetch:retrigger and fetch:clearOnRetrigger - Tracks user changes to prevent overwriting manual edits - Includes comprehensive unit tests and documentation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
Important This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior. Changed Packages
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4440 +/- ##
=======================================
Coverage 61.49% 61.49%
=======================================
Files 2534 2535 +1
Lines 101630 101696 +66
Branches 28453 28474 +21
=======================================
+ Hits 62496 62542 +46
- Misses 37301 37321 +20
Partials 1833 1833
*This pull request uses carry forward flags. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|



Story : https://redhat.atlassian.net/browse/RHIDP-16435
Summary
Adds a new ActiveBoolean widget that enables boolean (checkbox) fields in Orchestrator workflow forms to dynamically fetch their values from external APIs, following the same ui:props pattern as existing Active widgets (ActiveTextInput, ActiveDropdown, ActiveMultiSelect).
Changes
ActiveBooleanwidget componentorchestratorFormWidgets.mdFormDecoratorContent.tsxFeatures
fetch:urlto call external APIsfetch:response:valueJSONata selector to extract boolean valuesfetch:response:defaultas static fallback"true","false","1","0") and numbers (1,0) to booleanfetch:retriggerto refetch when other fields changefetch:clearOnRetriggerto clear value before refetchingreadOnlyschema propertyVideo:
Screen.Recording.2026-08-24.at.2.01.23.PM.mov
Usage Example
{ "enableFeature": { "type": "boolean", "title": "Enable Advanced Features", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "https://api.example.com/feature-flags", "fetch:response:value": "features.advanced.enabled", "fetch:response:default": false, "fetch:retrigger": ["current.tenantId"] } } }Testing Instructions
1. Setup Proxy Configuration
Add this to your
app-config.local.yaml:2. Create Test Workflow
Create the following files in your dev environment:
File:
packages/backend/.devModeTemp/repository/workflows/test-active-boolean.sw.yamlFile:
packages/backend/.devModeTemp/repository/workflows/schemas/test-active-boolean__main-schema.jsonClick to expand schema (169 lines)
{ "$id": "classpath:/schemas/test-active-boolean__main-schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "Test ActiveBoolean Widget", "type": "object", "description": "Testing ActiveBoolean widget with various fetch and ui:props configurations", "properties": { "basicTests": { "type": "object", "title": "Basic ActiveBoolean Tests", "description": "Test basic fetch capabilities of ActiveBoolean widget", "properties": { "basicFeature": { "type": "boolean", "title": "Basic Feature Flag", "description": "Fetches boolean value from API - uses stock > 50 as boolean", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/1", "fetch:response:value": "stock > 50" } }, "featureWithDefault": { "type": "boolean", "title": "Feature with Static Default", "description": "Has a static default that shows while fetch is loading", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/2", "fetch:response:value": "stock > 100", "fetch:response:default": true } }, "stringCoercion": { "type": "boolean", "title": "String to Boolean Coercion", "description": "API returns string, widget coerces to boolean", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/3", "fetch:response:value": "availabilityStatus != 'Low Stock'", "fetch:response:default": false } }, "numberCoercion": { "type": "boolean", "title": "Number to Boolean Coercion", "description": "API returns number, widget coerces to boolean", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/4", "fetch:response:value": "stock", "fetch:response:default": false } } } }, "retriggerTests": { "type": "object", "title": "Retrigger and Dependency Tests", "description": "Test fetch:retrigger and dependent boolean fields", "properties": { "tenantId": { "type": "string", "title": "Product ID (Simulating Tenant)", "description": "Select a product ID to see different boolean values", "enum": ["5", "10", "15"], "default": "5" }, "dependentFeature": { "type": "boolean", "title": "Tenant-Specific Feature", "description": "This boolean refetches when Product ID changes", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/$${{current.retriggerTests.tenantId}}", "fetch:response:value": "stock > 30", "fetch:retrigger": ["current.retriggerTests.tenantId"] } }, "clearOnRetriggerFeature": { "type": "boolean", "title": "Feature with Clear on Retrigger", "description": "Clears before refetching", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/$${{current.retriggerTests.tenantId}}", "fetch:response:value": "price > 500", "fetch:retrigger": ["current.retriggerTests.tenantId"], "fetch:clearOnRetrigger": true } } } }, "errorHandlingTests": { "type": "object", "title": "Error Handling Tests", "description": "Test error handling and retry behavior", "properties": { "silentErrorFeature": { "type": "boolean", "title": "Silent Error Handling", "description": "Errors are suppressed, shows default value", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/999999", "fetch:response:value": "stock > 0", "fetch:response:default": false, "fetch:error:silent": true } }, "retryFeature": { "type": "boolean", "title": "Feature with Retry", "description": "Retries up to 3 times with exponential backoff", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/6", "fetch:response:value": "stock > 0", "fetch:retry:maxAttempts": 3, "fetch:retry:delay": 1000, "fetch:retry:backoff": 2, "fetch:retry:statusCodes": [404, 408, 429, 500, 502, 503, 504] } } } }, "advancedTests": { "type": "object", "title": "Advanced Tests", "description": "Advanced scenarios and edge cases", "properties": { "readOnlyFeature": { "type": "boolean", "title": "Read-Only Feature Flag", "description": "This boolean is fetched but cannot be changed", "readOnly": true, "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/7", "fetch:response:value": "stock > 0" } }, "skipInitialFeature": { "type": "boolean", "title": "Skip Initial Value", "description": "Doesn't apply fetched value initially", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/1", "fetch:response:value": "stock > 50", "fetch:skipInitialValue": true } }, "complexSelectorFeature": { "type": "boolean", "title": "Complex JSONata Selector", "description": "Uses complex JSONata expression", "ui:widget": "ActiveBoolean", "ui:props": { "fetch:url": "$${{backend.baseUrl}}/api/proxy/dummyjson/products/8", "fetch:response:value": "(price > 100) and (stock > 10)", "fetch:response:default": false } } } } } }Supported ui:props
All standard Active widget props are supported:
fetch:url- API endpointfetch:response:value- JSONata selector for boolean valuefetch:response:default- Static fallback valuefetch:retrigger- Array of form field paths to watchfetch:clearOnRetrigger- Clear value before refetchingfetch:skipInitialValue- Don't apply initial fetch resultfetch:retry:maxAttempts- Number of retry attemptsfetch:retry:delay- Initial retry delay (ms)fetch:retry:backoff- Backoff multiplierfetch:retry:statusCodes- HTTP status codes to retryfetch:error:silent- Suppress error messagesfetch:method,fetch:headers,fetch:body- Request configuration