Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
41 changes: 41 additions & 0 deletions workspaces/orchestrator/docs/orchestratorFormWidgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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)) {
Expand All @@ -335,7 +350,7 @@ export function omitFromWorkflowInput(
}

propSchema = resolveSchema(propSchema as JSONSchema7, root);
if (shouldOmitFromWorkflowInput(propSchema)) {
if (shouldOmitFromWorkflowInput(propSchema, rootData)) {
continue;
}

Expand All @@ -349,6 +364,7 @@ export function omitFromWorkflowInput(
value as JsonObject,
propSchema as JSONSchema7,
root,
rootData,
);
continue;
}
Expand All @@ -359,7 +375,7 @@ export function omitFromWorkflowInput(
? resolveSchema(propSchema.items as JSONSchema7, root)
: undefined;

if (itemsSchema && shouldOmitFromWorkflowInput(itemsSchema)) {
if (itemsSchema && shouldOmitFromWorkflowInput(itemsSchema, rootData)) {
continue;
}

Expand All @@ -374,6 +390,7 @@ export function omitFromWorkflowInput(
item as JsonObject,
itemsSchema as JSONSchema7,
root,
rootData,
);
}
return item as JsonValue;
Expand Down