Fix field extension validation not working when field is in dependencies in an array field
Signed-off-by: bnechyporenko <bnechyporenko@bol.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Fix field extension validation not working when field is in dependencies in an array field
|
||||
+105
-2
@@ -17,6 +17,12 @@ import { JsonObject } from '@backstage/types';
|
||||
import { CustomFieldValidator } from '../../../extensions';
|
||||
import { createAsyncValidators } from './createAsyncValidators';
|
||||
|
||||
type UrlItem = {
|
||||
icon?: string;
|
||||
title?: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
describe('createAsyncValidators', () => {
|
||||
it('should call the correct functions for validation', async () => {
|
||||
const schema: JsonObject = {
|
||||
@@ -402,6 +408,9 @@ describe('createAsyncValidators', () => {
|
||||
});
|
||||
|
||||
it('should call validator for array object property from a custom field extension', async () => {
|
||||
let validatorTriggered = false;
|
||||
const validationErrors = [] as string[];
|
||||
|
||||
const schema: JsonObject = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -423,7 +432,23 @@ describe('createAsyncValidators', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
const validators = { CustomLinkField: jest.fn() };
|
||||
|
||||
const validators = {
|
||||
CustomLinkField: (input: unknown) => {
|
||||
const regex = /https?:\/\/(\S+)/;
|
||||
const items = input instanceof Array ? input : [input];
|
||||
|
||||
items.forEach((item: UrlItem) => {
|
||||
if (!regex.test(item.url)) {
|
||||
validationErrors.push(
|
||||
`Url ${item.url} is invalid. Urls must start with http:// or https://`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
validatorTriggered = true;
|
||||
},
|
||||
};
|
||||
|
||||
const validate = createAsyncValidators(schema, validators, {
|
||||
apiHolder: { get: jest.fn() },
|
||||
@@ -433,6 +458,84 @@ describe('createAsyncValidators', () => {
|
||||
links: [{ url: 'http://my-url.spotify.com' }],
|
||||
});
|
||||
|
||||
expect(validators.CustomLinkField).toHaveBeenCalled();
|
||||
expect(validatorTriggered).toBe(true);
|
||||
expect(validationErrors).toEqual([]);
|
||||
});
|
||||
|
||||
it('should validate field in the dependencies in an array field', async () => {
|
||||
const schema: JsonObject = {
|
||||
title: 'Make a choice',
|
||||
properties: {
|
||||
myArray: {
|
||||
type: 'array',
|
||||
title: 'Array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['selector'],
|
||||
properties: {
|
||||
selector: {
|
||||
title: 'Selector',
|
||||
type: 'string',
|
||||
enum: ['Choice 1', 'Choice 2'],
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
selector: {
|
||||
oneOf: [
|
||||
{ properties: { selector: { enum: ['Choice 1'] } } },
|
||||
{
|
||||
properties: {
|
||||
selector: { enum: ['Choice 2'] },
|
||||
customValidatedField: {
|
||||
title: 'Custom validated field',
|
||||
type: 'string',
|
||||
'ui:field': 'ValidateKebabCase',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const validatorsForChoice1 = { ValidateKebabCase: jest.fn() };
|
||||
|
||||
const validateChoice1 = createAsyncValidators(
|
||||
schema,
|
||||
validatorsForChoice1,
|
||||
{
|
||||
apiHolder: { get: jest.fn() },
|
||||
},
|
||||
);
|
||||
|
||||
await validateChoice1({
|
||||
myArray: [{ selector: 'Choice 1' }],
|
||||
});
|
||||
|
||||
expect(validatorsForChoice1.ValidateKebabCase).not.toHaveBeenCalled();
|
||||
|
||||
const validatorsForChoice2 = { ValidateKebabCase: jest.fn() };
|
||||
|
||||
const validateChoice2 = createAsyncValidators(
|
||||
schema,
|
||||
validatorsForChoice2,
|
||||
{
|
||||
apiHolder: { get: jest.fn() },
|
||||
},
|
||||
);
|
||||
|
||||
await validateChoice2({
|
||||
myArray: [
|
||||
{
|
||||
selector: 'Choice 2',
|
||||
customValidatedField: 'apple',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(validatorsForChoice2.ValidateKebabCase).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -125,6 +125,30 @@ export const createAsyncValidators = (
|
||||
}
|
||||
};
|
||||
|
||||
const doValidateDependency = async (propValue: JsonObject) => {
|
||||
const { schema: itemsSchema, uiSchema: itemsUiSchema } =
|
||||
extractSchemaFromStep(propValue);
|
||||
await doValidateItem(propValue, itemsSchema, itemsUiSchema);
|
||||
|
||||
const iterable = Array.isArray(value) ? value : [value];
|
||||
for (const item of iterable) {
|
||||
if (item && isObject(item)) {
|
||||
const keys = Object.keys(item);
|
||||
for (const k of keys) {
|
||||
if (itemsUiSchema[k] && 'ui:field' in itemsUiSchema[k]) {
|
||||
await validateForm(
|
||||
itemsUiSchema[k]['ui:field'],
|
||||
k,
|
||||
item[k],
|
||||
itemsSchema,
|
||||
itemsUiSchema,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if ('ui:field' in definitionInSchema) {
|
||||
await doValidateItem(definitionInSchema, schema, uiSchema);
|
||||
} else if (hasItems && 'ui:field' in definitionInSchema.items) {
|
||||
@@ -135,6 +159,12 @@ export const createAsyncValidators = (
|
||||
for (const [, propValue] of Object.entries(properties)) {
|
||||
await doValidate(propValue);
|
||||
}
|
||||
|
||||
const dependencies = (definitionInSchema.items?.dependencies ??
|
||||
[]) as JsonObject[];
|
||||
for (const [, propValue] of Object.entries(dependencies)) {
|
||||
await doValidateDependency(propValue);
|
||||
}
|
||||
} else if (isObject(value)) {
|
||||
formValidation[key] = await validate(formData, pointer, value);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user