Added a context parameter to validator functions, letting them have access to the API holder

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2021-07-07 13:27:39 +02:00
parent bbd2ab80a3
commit 02b962394b
8 changed files with 66 additions and 15 deletions
+13
View File
@@ -4,6 +4,7 @@
```ts
import { ApiHolder } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/core-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { DiscoveryApi } from '@backstage/core-plugin-api';
@@ -24,9 +25,21 @@ import { ScmIntegrationRegistry } from '@backstage/integration';
// @public (undocumented)
export function createScaffolderFieldExtension<T = any>(options: FieldExtensionOptions<T>): Extension<() => null>;
// @public (undocumented)
export type CustomFieldValidator<T> = ((data: T, field: FieldValidation) => void) | ((data: T, field: FieldValidation, context: {
apiHolder: ApiHolder;
}) => void);
// @public (undocumented)
export const EntityPickerFieldExtension: () => null;
// @public (undocumented)
export type FieldExtensionOptions<T = any> = {
name: string;
component: (props: FieldProps<T>) => JSX.Element | null;
validation?: CustomFieldValidator<T>;
};
// @public (undocumented)
export const OwnerPickerFieldExtension: () => null;
@@ -15,13 +15,13 @@
*/
import { JsonObject, JsonValue } from '@backstage/config';
import { LinearProgress } from '@material-ui/core';
import { FieldValidation, FormValidation, IChangeEvent } from '@rjsf/core';
import { FormValidation, IChangeEvent } from '@rjsf/core';
import React, { useCallback, useState } from 'react';
import { generatePath, Navigate, useNavigate } from 'react-router';
import { useParams } from 'react-router-dom';
import { useAsync } from 'react-use';
import { scaffolderApiRef } from '../../api';
import { FieldExtensionOptions } from '../../extensions';
import { CustomFieldValidator, FieldExtensionOptions } from '../../extensions';
import { rootRouteRef } from '../../routes';
import { MultistepJsonForm } from '../MultistepJsonForm';
@@ -32,7 +32,13 @@ import {
Lifecycle,
Page,
} from '@backstage/core-components';
import { errorApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
import {
ApiHolder,
errorApiRef,
useApi,
useApiHolder,
useRouteRef,
} from '@backstage/core-plugin-api';
const useTemplateParameterSchema = (templateName: string) => {
const scaffolderApi = useApi(scaffolderApiRef);
@@ -54,10 +60,10 @@ function isObject(obj: unknown): obj is JsonObject {
export const createValidator = (
rootSchema: JsonObject,
validators: Record<
string,
undefined | ((value: JsonValue, validation: FieldValidation) => void)
>,
validators: Record<string, undefined | CustomFieldValidator<unknown>>,
context: {
apiHolder: ApiHolder;
},
) => {
function validate(
schema: JsonObject,
@@ -86,7 +92,11 @@ export const createValidator = (
const fieldName =
isObject(propSchema) && (propSchema['ui:field'] as string);
if (fieldName && typeof validators[fieldName] === 'function') {
validators[fieldName]!(propData as JsonValue, propValidation);
validators[fieldName]!(
propData as JsonValue,
propValidation,
context,
);
}
}
}
@@ -103,6 +113,7 @@ export const TemplatePage = ({
}: {
customFieldExtensions?: FieldExtensionOptions[];
}) => {
const apiHolder = useApiHolder();
const errorApi = useApi(errorApiRef);
const scaffolderApi = useApi(scaffolderApiRef);
const { templateName } = useParams();
@@ -171,7 +182,11 @@ export const TemplatePage = ({
steps={schema.steps.map(step => {
return {
...step,
validate: createValidator(step.schema, customFieldValidators),
validate: createValidator(
step.schema,
customFieldValidators,
{ apiHolder },
),
};
})}
/>
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { repoPickerValidation } from './validation';
import { FieldValidation } from '@rjsf/core';
@@ -22,7 +23,7 @@ describe('RepoPicker Validation', () => {
addError: jest.fn(),
} as unknown) as FieldValidation);
it('validaties when no repo', () => {
it('validates when no repo', () => {
const mockFieldValidation = fieldValidator();
repoPickerValidation('github.com?owner=a', mockFieldValidation);
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldValidation } from '@rjsf/core';
export const repoPickerValidation = (
+2 -2
View File
@@ -15,7 +15,7 @@
*/
import React from 'react';
import { FieldExtensionOptions } from './types';
import { CustomFieldValidator, FieldExtensionOptions } from './types';
import { Extension, attachComponentData } from '@backstage/core-plugin-api';
export const FIELD_EXTENSION_WRAPPER_KEY = 'scaffolder.extensions.wrapper.v1';
@@ -46,6 +46,6 @@ attachComponentData(
true,
);
export type { FieldExtensionOptions };
export type { CustomFieldValidator, FieldExtensionOptions };
export { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from './default';
+10 -1
View File
@@ -13,10 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ApiHolder } from '@backstage/core-plugin-api';
import { FieldValidation, FieldProps } from '@rjsf/core';
export type CustomFieldValidator<T> =
| ((data: T, field: FieldValidation) => void)
| ((
data: T,
field: FieldValidation,
context: { apiHolder: ApiHolder },
) => void);
export type FieldExtensionOptions<T = any> = {
name: string;
component: (props: FieldProps<T>) => JSX.Element | null;
validation?: (data: T, field: FieldValidation) => void;
validation?: CustomFieldValidator<T>;
};
+4 -2
View File
@@ -16,9 +16,11 @@
export { scaffolderApiRef, ScaffolderClient } from './api';
export type { ScaffolderApi } from './api';
export {
createScaffolderFieldExtension,
export { createScaffolderFieldExtension } from './extensions';
export type {
ScaffolderFieldExtensions,
CustomFieldValidator,
FieldExtensionOptions,
} from './extensions';
export {
EntityPickerFieldExtension,