Skip to content

Commit 112d44f

Browse files
authored
feat(orchestrator): add support for template arrays (#1003)
Signed-off-by: Marek Libra <marek.libra@gmail.com>
1 parent d840fb0 commit 112d44f

7 files changed

Lines changed: 129 additions & 31 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-orchestrator-form-widgets': patch
3+
---
4+
5+
Adding suuport for template arrays for the Orchestrator widgets.

workspaces/orchestrator/docs/orchestratorFormWidgets.md

Lines changed: 19 additions & 18 deletions
Large diffs are not rendered by default.

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/evaluateTemplate.test.ts

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { JsonValue } from '@backstage/types/index';
1718
import { evaluateTemplate, evaluateTemplateProps } from './evaluateTemplate';
1819

1920
const unitEvaluator: evaluateTemplateProps['unitEvaluator'] = async (
@@ -33,15 +34,52 @@ describe('evaluate template', () => {
3334

3435
it('fails on incorrect input', async () => {
3536
const cases = [
36-
{ input: undefined, throws: 'Template can be a string only, key: myKey' },
37+
{
38+
input: undefined,
39+
throws: 'Template can be either a string or an array, key: myKey',
40+
},
3741
{ input: '$${{}}', throws: 'Template unit can not be empty' },
3842
{ input: '$${{foo', throws: 'Template unit is not closed by }}' },
43+
{
44+
input: ['constant', []],
45+
throws:
46+
'Items of array templates can be strings only, template: "["constant",[]]"',
47+
},
48+
{
49+
input: [[]],
50+
throws:
51+
'Items of array templates can be strings only, template: "[[]]"',
52+
},
53+
{
54+
input: [{}],
55+
throws:
56+
'Items of array templates can be strings only, template: "[{}]"',
57+
},
58+
{
59+
input: ['constant', {}],
60+
throws:
61+
'Items of array templates can be strings only, template: "["constant",{}]"',
62+
},
63+
{
64+
input: [undefined],
65+
throws:
66+
'Items of array templates can be strings only, template: "[null]"',
67+
},
68+
{
69+
input: ['constant', undefined],
70+
throws:
71+
'Items of array templates can be strings only, template: "["constant",null]"',
72+
},
3973
];
4074

4175
await Promise.all(
4276
cases.map(c =>
4377
expect(
44-
evaluateTemplate({ ...props, template: c.input }),
78+
evaluateTemplate({
79+
...props,
80+
template:
81+
c.input as JsonValue /* retype since we are testing malformed inputs */,
82+
}),
4583
).rejects.toThrow(c.throws),
4684
),
4785
);
@@ -74,4 +112,29 @@ describe('evaluate template', () => {
74112
evaluateTemplate({ ...props, template: 'a$${{foo}}$${{$${{xx}}}}b' }),
75113
).resolves.toBe('afoo$${{xx}}b');
76114
});
115+
116+
it('can parse array templates', async () => {
117+
const cases = [
118+
{ input: [], expected: [] },
119+
{ input: [''], expected: [''] },
120+
{
121+
input: ['constantA', 'constantB'],
122+
expected: ['constantA', 'constantB'],
123+
},
124+
{ input: ['$${{foo}}'], expected: ['foo'] },
125+
{
126+
input: ['$${{foo}}', 'constant', '$${{bar}}'],
127+
expected: ['foo', 'constant', 'bar'],
128+
},
129+
{ input: ['$${{foo}}$${{bar}}'], expected: ['foobar'] },
130+
];
131+
132+
await Promise.all(
133+
cases.map(c =>
134+
expect(
135+
evaluateTemplate({ ...props, template: c.input }),
136+
).resolves.toStrictEqual(c.expected),
137+
),
138+
);
139+
});
77140
});

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/evaluateTemplate.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export type evaluateTemplateProps = {
3333
uiProps?: UiProps;
3434
};
3535

36-
export const evaluateTemplate = async (
36+
export const evaluateTemplateString = async (
3737
props: evaluateTemplateProps,
3838
): Promise<string> => {
3939
const { template, key, unitEvaluator, formData, responseData, uiProps } =
@@ -72,7 +72,7 @@ export const evaluateTemplate = async (
7272
}
7373

7474
if (template.length > stopIndex + 2) {
75-
evaluated += await evaluateTemplate({
75+
evaluated += await evaluateTemplateString({
7676
...props,
7777
template: template.substring(stopIndex + 2),
7878
});
@@ -82,6 +82,31 @@ export const evaluateTemplate = async (
8282
return evaluated;
8383
};
8484

85+
export const evaluateTemplate = async (
86+
props: evaluateTemplateProps,
87+
): Promise<string | string[]> => {
88+
const { template, ...restProps } = props;
89+
const { key } = restProps;
90+
91+
if (Array.isArray(template)) {
92+
if (!template.every(item => typeof item === 'string')) {
93+
throw new Error(
94+
`Items of array templates can be strings only, template: "${JSON.stringify(template)}"`,
95+
);
96+
}
97+
98+
return await Promise.all(
99+
template.map(item =>
100+
evaluateTemplateString({ template: item, ...restProps }),
101+
),
102+
);
103+
} else if (typeof template === 'string') {
104+
return evaluateTemplateString(props);
105+
}
106+
107+
throw new Error(`Template can be either a string or an array, key: ${key}`);
108+
};
109+
85110
export const useEvaluateTemplate = ({
86111
template,
87112
key,
@@ -92,12 +117,12 @@ export const useEvaluateTemplate = ({
92117
key: string;
93118
formData: JsonObject;
94119
setError: (e: string) => void;
95-
}) => {
120+
}): string | undefined => {
96121
const unitEvaluator = useTemplateUnitEvaluator();
97122
const [evaluated, setEvaluated] = useState<string>();
98123

99124
useEffect(() => {
100-
evaluateTemplate({ template, key, unitEvaluator, formData })
125+
evaluateTemplateString({ template, key, unitEvaluator, formData })
101126
.then(setEvaluated)
102127
.catch(reason => setError(reason.toString()));
103128
}, [template, unitEvaluator, formData, key, setError]);

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useFetchAndEvaluate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { JsonObject } from '@backstage/types/index';
1818
import React, { useState } from 'react';
1919
import { UiProps } from '../uiPropTypes';
2020
import { getErrorMessage } from './errorUtils';
21-
import { evaluateTemplate } from './evaluateTemplate';
21+
import { evaluateTemplateString } from './evaluateTemplate';
2222
import { useFetch } from './useFetch';
2323
import { useRetriggerEvaluate } from './useRetriggerEvaluate';
2424
import { useTemplateUnitEvaluator } from './useTemplateUnitEvaluator';
@@ -54,7 +54,7 @@ export const useFetchAndEvaluate = (
5454
try {
5555
setLoading(true);
5656
setError(undefined);
57-
const evaluatedText = await evaluateTemplate({
57+
const evaluatedText = await evaluateTemplateString({
5858
template,
5959
key: fieldId,
6060
unitEvaluator,
@@ -91,6 +91,6 @@ export const useFetchAndEvaluate = (
9191
return {
9292
text: resultText,
9393
loading: loading || fetchLoading,
94-
error: error || fetchError,
94+
error: error ?? fetchError,
9595
};
9696
};

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useGetExtraErrors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
import { JsonObject } from '@backstage/types';
2424
import { ERRORS_KEY, ErrorSchema } from '@rjsf/utils';
2525
import { useTemplateUnitEvaluator } from './useTemplateUnitEvaluator';
26-
import { evaluateTemplate } from './evaluateTemplate';
26+
import { evaluateTemplateString } from './evaluateTemplate';
2727
import { getRequestInit } from './useRequestInit';
2828
import { safeSet } from './safeSet';
2929

@@ -86,7 +86,7 @@ export const useGetExtraErrors = () => {
8686
) {
8787
const value = get(formData, path);
8888
if (value !== undefined) {
89-
const evaluatedValidateUrl = await evaluateTemplate({
89+
const evaluatedValidateUrl = await evaluateTemplateString({
9090
template: validateUrl,
9191
key: 'validate:url',
9292
unitEvaluator: templateUnitEvaluator,

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useRequestInit.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
import { useEffect, useState } from 'react';
1717
import { isEqual } from 'lodash';
1818
import { JsonObject } from '@backstage/types';
19-
import { evaluateTemplate, evaluateTemplateProps } from './evaluateTemplate';
19+
import {
20+
evaluateTemplate,
21+
evaluateTemplateProps,
22+
evaluateTemplateString,
23+
} from './evaluateTemplate';
2024
import { useTemplateUnitEvaluator } from './useTemplateUnitEvaluator';
2125

2226
const ALLOWED_METHODS = ['GET', 'POST'];
@@ -83,7 +87,7 @@ export const getRequestInit = async (
8387
const keys = Object.keys(headers);
8488
const values = await Promise.all(
8589
keys.map(key =>
86-
evaluateTemplate({
90+
evaluateTemplateString({
8791
unitEvaluator,
8892
key,
8993
formData,

0 commit comments

Comments
 (0)