Added a missing validator check for items in an array

Signed-off-by: bnechyporenko <bnechyporenko@bol.com>
This commit is contained in:
bnechyporenko
2023-02-06 13:41:18 +01:00
parent a37fb186f0
commit 7ad74fba73
2 changed files with 79 additions and 6 deletions
@@ -17,14 +17,32 @@
import { createValidator } from './createValidator';
import { CustomFieldValidator } from '@backstage/plugin-scaffolder-react';
import { ApiHolder } from '@backstage/core-plugin-api';
import { FormValidation } from '@rjsf/core';
import { FieldValidation, FormValidation } from '@rjsf/core';
describe('createValidator', () => {
const validators: Record<string, undefined | CustomFieldValidator<unknown>> =
{
CustomPicker: (value, validation, _context) => {
CustomPicker: (
value: unknown,
fieldValidation: FieldValidation,
_context: { apiHolder: ApiHolder },
) => {
if (!value || !(value as { value?: unknown }).value) {
validation.addError('Error !');
fieldValidation.addError('Error !');
}
},
TagPicker: (
value: unknown,
fieldValidation: FieldValidation,
_context: { apiHolder: ApiHolder },
) => {
if (!value) {
fieldValidation.addError('A tag name can not be empty');
}
if (!/^[a-z0-9-]+$/.test(value as string)) {
fieldValidation.addError(
'A tag name can only contain lowercase letters, numeric characters or dashes',
);
}
},
};
@@ -70,4 +88,39 @@ describe('createValidator', () => {
expect(result).not.toBeNull();
expect(result.p1.addError).toHaveBeenCalledTimes(1);
});
it('Should call validator for array property from a custom field extension', () => {
/* GIVEN */
const rootSchema = {
title: 'My form',
properties: {
tags: {
title: 'Tags',
type: 'array',
items: {
type: 'string',
'ui:field': 'TagPicker',
},
},
},
};
const validator = createValidator(rootSchema, validators, context);
const formData = {
tags: ['invalid-tag$$'],
};
const errors = {
addError: jest.fn(),
tags: {
addError: jest.fn(),
} as unknown as FormValidation,
} as unknown as FormValidation;
/* WHEN */
const result = validator(formData, errors);
/* THEN */
expect(result).not.toBeNull();
expect(result.tags.addError).toHaveBeenCalledTimes(1);
});
});
@@ -23,6 +23,10 @@ function isObject(obj: unknown): obj is JsonObject {
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
}
function isArray(obj: unknown): obj is JsonObject {
return typeof obj === 'object' && obj !== null && Array.isArray(obj);
}
export const createValidator = (
rootSchema: JsonObject,
validators: Record<string, undefined | CustomFieldValidator<unknown>>,
@@ -46,8 +50,8 @@ export const createValidator = (
for (const [key, propData] of Object.entries(formData)) {
const propValidation = errors[key];
const propSchemaProps = schemaProps[key];
if (isObject(propData)) {
const propSchemaProps = schemaProps[key];
if (isObject(propSchemaProps)) {
validate(
propSchemaProps,
@@ -55,10 +59,26 @@ export const createValidator = (
propValidation as FormValidation,
);
}
} else if (isArray(propData)) {
if (isObject(propSchemaProps)) {
const { items } = propSchemaProps;
if (isObject(items)) {
for (const propItem of propData as JsonObject[]) {
const fieldName = items['ui:field'] as string;
if (fieldName && typeof validators[fieldName] === 'function') {
validators[fieldName]!(
propItem as JsonValue,
propValidation,
context,
);
}
}
}
}
} else {
const propSchema = schemaProps[key];
const fieldName =
isObject(propSchema) && (propSchema['ui:field'] as string);
isObject(propSchemaProps) &&
(propSchemaProps['ui:field'] as string);
if (fieldName && typeof validators[fieldName] === 'function') {
validators[fieldName]!(
propData as JsonValue,