Skip to content
Open
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
5 changes: 5 additions & 0 deletions workspaces/orchestrator/.changeset/fetch-error-silent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-orchestrator-form-widgets': patch
---

Add fetch:error:silent support to suppress fetch errors in widgets.
5 changes: 5 additions & 0 deletions workspaces/orchestrator/docs/orchestratorFormWidgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ The widget supports following `ui:props`:
- fetch:body
- fetch:retrigger
- fetch:error:ignoreUnready
- fetch:error:silent
- fetch:response:value
- fetch:response:mandatory

Expand Down Expand Up @@ -300,6 +301,7 @@ The widget supports following `ui:props`:
- fetch:body
- fetch:retrigger
- fetch:error:ignoreUnready
- fetch:error:silent
- fetch:response:value
- fetch:response:default
- fetch:response:autocomplete
Expand Down Expand Up @@ -340,6 +342,7 @@ The widget supports following `ui:props`:
- fetch:body
- fetch:retrigger
- fetch:error:ignoreUnready
- fetch:error:silent
- fetch:response:value
- fetch:response:default
- fetch:response:label
Expand Down Expand Up @@ -389,6 +392,7 @@ The widget supports following `ui:props`:
- fetch:body
- fetch:retrigger
- fetch:error:ignoreUnready
- fetch:error:silent
- fetch:response:autocomplete
- fetch:response:mandatory
- fetch:response:value
Expand Down Expand Up @@ -527,6 +531,7 @@ Various selectors (like `fetch:response:*`) are processed by the [jsonata](https
| fetch:body | An object representing the body of an HTTP POST request. Not used with the GET method. Property value can be a string template or an array of strings. templates. | `{“foo”: “bar $${{identityApi.token}}”, "myArray": ["constant", "$${{current.solutionName}}"]}` |
| fetch:retrigger | An array of keys/key families as described in the Backstage API Exposed Parts. If the value referenced by any key from this list is changed, the fetch is triggered. | `["current.solutionName", "identityApi.profileName"]` |
| fetch:error:ignoreUnready | When set to `true`, suppresses fetch error display until all `fetch:retrigger` dependencies have non-empty values. This is useful when fetch depends on other fields that are not filled yet, preventing expected errors from being displayed during initial load. | `true`, `false` (default: `false`) |
| fetch:error:silent | When set to `true`, suppresses fetch error display when the fetch request returns a non-OK status (4xx/5xx). Use this when you want to handle error states via conditional UI instead of showing the widget error. | `true`, `false` (default: `false`) |
| fetch:response:default | A static default value that is applied immediately when the widget mounts, before any fetch completes. Acts as a fallback when fetch fails or has not completed yet. Gets overridden by `fetch:response:value` once fetch succeeds. For ActiveTextInput/ActiveDropdown use a string, for ActiveMultiSelect use a string array. | `"create"` (string) or `["tag1", "tag2"]` (array) |
| fetch:response:\[YOUR_KEY\] | A JSONata selector (string) or object value for extracting data from the fetch response. There can be any count of the \[YOUR_KEY\] properties, so a single fetch response can be used to retrieve multiple records. Supports both string selectors and object type values. | Account.Order.Product.ProductID |
| fetch:response:label | Special (well-known) case of the fetch:response:\[YOUR_KEY\] . Used i.e. by the ActiveDropdown to label the items. | |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type UiProps = {
'fetch:body'?: Record<string, JsonValue>;
'fetch:retrigger'?: string[];
'fetch:error:ignoreUnready'?: boolean;
'fetch:error:silent'?: boolean;
[key: `fetch:response:${string}`]: JsonValue;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const useFetchAndEvaluate = (
return {
text: resultText,
loading: completeLoading,
error: error ?? fetchError,
error,
fetchError,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -156,21 +156,39 @@ export const ActiveDropdown: Widget<
}
}, [handleChange, value, values, isChangedByUser, staticDefaultValue]);

if (localError ?? error) {
return <ErrorText text={localError ?? error ?? ''} id={id} />;
const shouldShowFetchError = uiProps['fetch:error:silent'] !== true;
const suppressFetchError = !shouldShowFetchError && !!error;
const displayError = localError ?? (shouldShowFetchError ? error : undefined);
if (displayError) {
return <ErrorText text={displayError} id={id} />;
}

// Compute display options: use fetched options, or fall back to static default
const hasOptions = labels && labels.length > 0 && values && values.length > 0;
const hasFallbackDefault = !hasOptions && staticDefaultValue;

// Show loading only if we have no options AND no fallback default
if (completeLoading && !hasFallbackDefault) {
if (completeLoading && !hasFallbackDefault && !suppressFetchError) {
return <CircularProgress size={20} />;
}

// If still loading but no options yet and no fallback, show spinner
if (!hasOptions && !hasFallbackDefault) {
if (suppressFetchError) {
return (
<FormControl variant="outlined" fullWidth>
<InputLabel id={labelId}>{label}</InputLabel>
<Select
labelId={labelId}
id={id}
data-testid={id}
value={value ?? ''}
label={label}
disabled
/>
</FormControl>
);
}
return <CircularProgress size={20} />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,20 +252,25 @@ export const ActiveMultiSelect: Widget<
}
};

if (localError ?? error) {
return <ErrorText text={localError ?? error ?? ''} id={id} />;
const shouldShowFetchError = uiProps['fetch:error:silent'] !== true;
const suppressFetchError = !shouldShowFetchError && !!error;
const displayError = localError ?? (shouldShowFetchError ? error : undefined);
if (displayError) {
return <ErrorText text={displayError} id={id} />;
}

// Show spinner only if loading AND we don't have static defaults to show
const hasStaticDefaults =
staticDefaultValues && staticDefaultValues.length > 0;
if (completeLoading && !hasStaticDefaults) {
if (completeLoading && !hasStaticDefaults && !suppressFetchError) {
return <CircularProgress size={20} />;
}

// Render if we have fetched options, static defaults, or current values
const hasOptionsToShow =
allOptions.length > 0 || autocompleteOptions !== undefined;
allOptions.length > 0 ||
autocompleteOptions !== undefined ||
suppressFetchError;
if (hasOptionsToShow) {
return (
<Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const ActiveText: Widget<
const handleFetchStarted = formContext?.handleFetchStarted;
const handleFetchEnded = formContext?.handleFetchEnded;

const { text, error, loading } = useFetchAndEvaluate(
const { text, error, fetchError, loading } = useFetchAndEvaluate(
uiProps['ui:text'] ?? '',
formData ?? {},
uiProps,
Expand All @@ -55,8 +55,9 @@ export const ActiveText: Widget<
);
}

if (error) {
return <ErrorText id={id} text={error} />;
const shouldShowFetchError = uiProps['fetch:error:silent'] !== true;
if (error ?? (shouldShowFetchError ? fetchError : undefined)) {
return <ErrorText id={id} text={error ?? fetchError ?? ''} />;
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@ export const ActiveTextInput: Widget<
wrapProcessing,
]);

if (localError ?? error) {
return <ErrorText text={localError ?? error ?? ''} id={id} />;
const shouldShowFetchError = uiProps['fetch:error:silent'] !== true;
const displayError = localError ?? (shouldShowFetchError ? error : undefined);
if (displayError) {
return <ErrorText text={displayError} id={id} />;
}

// Show loading only if we don't have a static default value to display
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,10 @@ export const SchemaUpdater: Widget<
doItAsync();
}, [data, props.id, updateSchema, valueSelector, wrapProcessing]);

if (localError ?? error) {
return <ErrorText text={localError ?? error ?? ''} id={id} />;
const shouldShowFetchError = uiProps['fetch:error:silent'] !== true;
const displayError = localError ?? (shouldShowFetchError ? error : undefined);
if (displayError) {
return <ErrorText text={displayError} id={id} />;
}

if (completeLoading) {
Expand Down