diff --git a/workspaces/orchestrator/.changeset/omit-from-workflow-input-conditions.md b/workspaces/orchestrator/.changeset/omit-from-workflow-input-conditions.md new file mode 100644 index 0000000000..d798ae7021 --- /dev/null +++ b/workspaces/orchestrator/.changeset/omit-from-workflow-input-conditions.md @@ -0,0 +1,6 @@ +--- +'@red-hat-developer-hub/backstage-plugin-orchestrator-form-react': patch +--- + +Allow `omitFromWorkflowInput` to be conditional using the same expression +format as `ui:hidden` so fields can be omitted based on form data. diff --git a/workspaces/orchestrator/docs/orchestratorFormWidgets.md b/workspaces/orchestrator/docs/orchestratorFormWidgets.md index 14a8b0e237..29434f04c6 100644 --- a/workspaces/orchestrator/docs/orchestratorFormWidgets.md +++ b/workspaces/orchestrator/docs/orchestratorFormWidgets.md @@ -970,6 +970,47 @@ Hidden fields (regardless of hiding method): If all inputs within a multi-step form's step are marked with `ui:hidden` (either statically or dynamically), the entire step will be automatically hidden from the stepper navigation. The step and its hidden fields will still be processed during form submission. +## Omitting Fields from Workflow Input + +Use `omitFromWorkflowInput` to exclude fields from the **execution payload** while +keeping them in form state (and therefore still visible in the review step). + +The property supports the same formats as `ui:hidden`: + +### Static Omit + +```json +{ + "secret": { + "type": "string", + "title": "Secret", + "omitFromWorkflowInput": true + } +} +``` + +### Conditional Omit + +```json +{ + "mode": { + "type": "string", + "enum": ["simple", "advanced"] + }, + "advancedSecret": { + "type": "string", + "title": "Advanced Secret", + "omitFromWorkflowInput": { + "when": "mode", + "is": "advanced" + } + } +} +``` + +Supported condition patterns are identical to `ui:hidden` (e.g., `when/is`, +`when/isNot`, `when/isEmpty`, `allOf`, `anyOf`). + ## Customization The recommended approach for introducing customizations or other modifications is to contribute directly to [the library on GitHub](https://github.com/redhat-developer/rhdh-plugins/tree/main/workspaces/orchestrator/plugins/orchestrator-form-widgets). diff --git a/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.test.ts b/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.test.ts index 8d8f2e0b0b..8aac9497ae 100644 --- a/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.test.ts +++ b/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.test.ts @@ -784,5 +784,56 @@ describe('pruneFormData', () => { }); expect(result.step).not.toHaveProperty('secret'); }); + + it('should remove properties when omitFromWorkflowInput condition matches', () => { + const schema: JSONSchema7 = { + type: 'object', + properties: { + mode: { type: 'string' }, + conditional: { + type: 'string', + omitFromWorkflowInput: { + when: 'mode', + is: 'advanced', + }, + } as JSONSchema7, + }, + }; + + const formData = { + mode: 'advanced', + conditional: 'omit', + }; + + const result = omitFromWorkflowInput(formData, schema); + + expect(result).toEqual({ mode: 'advanced' }); + expect(result).not.toHaveProperty('conditional'); + }); + + it('should keep properties when omitFromWorkflowInput condition does not match', () => { + const schema: JSONSchema7 = { + type: 'object', + properties: { + mode: { type: 'string' }, + conditional: { + type: 'string', + omitFromWorkflowInput: { + when: 'mode', + is: 'advanced', + }, + } as JSONSchema7, + }, + }; + + const formData = { + mode: 'simple', + conditional: 'keep', + }; + + const result = omitFromWorkflowInput(formData, schema); + + expect(result).toEqual({ mode: 'simple', conditional: 'keep' }); + }); }); }); diff --git a/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.ts b/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.ts index 1276eca808..05ec389f4b 100644 --- a/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.ts +++ b/workspaces/orchestrator/plugins/orchestrator-form-react/src/utils/pruneFormData.ts @@ -18,8 +18,11 @@ import { JsonObject, JsonValue } from '@backstage/types'; import type { JSONSchema7 } from 'json-schema'; +import { HiddenCondition } from '../types/HiddenCondition'; +import { evaluateHiddenCondition } from './evaluateHiddenCondition'; + type WorkflowInputSchema = JSONSchema7 & { - omitFromWorkflowInput?: boolean; + omitFromWorkflowInput?: HiddenCondition; }; /** @@ -44,8 +47,18 @@ function resolveSchema( return schema; } -function shouldOmitFromWorkflowInput(schema: JSONSchema7): boolean { - return (schema as WorkflowInputSchema).omitFromWorkflowInput === true; +function shouldOmitFromWorkflowInput( + schema: JSONSchema7, + rootFormData: JsonObject, +): boolean { + const omitFlag = (schema as WorkflowInputSchema).omitFromWorkflowInput; + if (omitFlag === undefined) { + return false; + } + if (typeof omitFlag === 'boolean') { + return omitFlag; + } + return evaluateHiddenCondition(omitFlag, rootFormData); } /** @@ -321,8 +334,10 @@ export function omitFromWorkflowInput( formData: JsonObject, schema: JSONSchema7, rootSchema?: JSONSchema7, + rootFormData?: JsonObject, ): JsonObject { const root = rootSchema || schema; + const rootData = rootFormData || formData; const filtered: JsonObject = {}; for (const [key, value] of Object.entries(formData)) { @@ -335,7 +350,7 @@ export function omitFromWorkflowInput( } propSchema = resolveSchema(propSchema as JSONSchema7, root); - if (shouldOmitFromWorkflowInput(propSchema)) { + if (shouldOmitFromWorkflowInput(propSchema, rootData)) { continue; } @@ -349,6 +364,7 @@ export function omitFromWorkflowInput( value as JsonObject, propSchema as JSONSchema7, root, + rootData, ); continue; } @@ -359,7 +375,7 @@ export function omitFromWorkflowInput( ? resolveSchema(propSchema.items as JSONSchema7, root) : undefined; - if (itemsSchema && shouldOmitFromWorkflowInput(itemsSchema)) { + if (itemsSchema && shouldOmitFromWorkflowInput(itemsSchema, rootData)) { continue; } @@ -374,6 +390,7 @@ export function omitFromWorkflowInput( item as JsonObject, itemsSchema as JSONSchema7, root, + rootData, ); } return item as JsonValue;