chore: added some more refactor to validate now TODO

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2021-05-15 22:10:26 +02:00
parent 73a0f4e52b
commit 6bba66c0d6
3 changed files with 56 additions and 58 deletions
@@ -24,7 +24,12 @@ import {
useRouteRef,
} from '@backstage/core';
import { LinearProgress } from '@material-ui/core';
import { FormValidation, IChangeEvent } from '@rjsf/core';
import {
Field,
FieldValidation,
FormValidation,
IChangeEvent,
} from '@rjsf/core';
import parseGitUrl from 'git-url-parse';
import React, { useCallback, useContext, useState } from 'react';
import { generatePath, useNavigate, Navigate } from 'react-router';
@@ -34,7 +39,7 @@ import { scaffolderApiRef } from '../../api';
import { rootRouteRef } from '../../routes';
import { MultistepJsonForm } from '../MultistepJsonForm';
import { RepoUrlPicker, OwnerPicker } from '../fields';
import { JsonObject } from '@backstage/config';
import { JsonObject, JsonValue } from '@backstage/config';
import { ExtensionContext } from '../../extensions';
const useTemplateParameterSchema = (templateName: string) => {
@@ -55,7 +60,10 @@ function isObject(obj: unknown): obj is JsonObject {
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
}
export const createValidator = (rootSchema: JsonObject) => {
export const createValidator = (
rootSchema: JsonObject,
validators: Record<string, (value: any, validation: FieldValidation) => void>,
) => {
function validate(
schema: JsonObject,
formData: JsonObject,
@@ -67,7 +75,7 @@ export const createValidator = (rootSchema: JsonObject) => {
}
for (const [key, propData] of Object.entries(formData)) {
const propErrors = errors[key];
const propValidation = errors[key];
if (isObject(propData)) {
const propSchemaProps = schemaProps[key];
@@ -75,27 +83,16 @@ export const createValidator = (rootSchema: JsonObject) => {
validate(
propSchemaProps,
propData as JsonObject,
propErrors as FormValidation,
propValidation as FormValidation,
);
}
} else {
const propSchema = schemaProps[key];
if (
isObject(propSchema) &&
propSchema['ui:field'] === 'RepoUrlPicker'
) {
try {
const { host, searchParams } = new URL(`https://${propData}`);
if (
!host ||
!searchParams.get('owner') ||
!searchParams.get('repo')
) {
propErrors.addError('Incomplete repository location provided');
}
} catch {
propErrors.addError('Unable to parse the Repository URL');
}
const fieldName =
isObject(propSchema) && (propSchema['ui:field'] as string);
const validator = fieldName && validators[fieldName];
if (validator) {
validator(propData, propValidation);
}
}
}
@@ -175,12 +172,18 @@ export const TemplatePage = () => {
return <Navigate to={rootLink()} />;
}
const customFields = state!.fields.reduce((acc, next) => {
acc[next.name] = next.component;
return acc;
}, {} as Record<string, any>);
const { components, validation } = state!.fields.reduce(
(acc, next) => {
acc.components[next.name] = next.component;
acc.validation[next.name] = next.validation;
return acc;
},
{ components: {}, validation: {} } as {
components: Record<string, Field>;
validation: Record<string, (data: JsonValue, f: FieldValidation) => void>;
},
);
console.log(customFields);
return (
<Page themeId="home">
<Header
@@ -202,7 +205,7 @@ export const TemplatePage = () => {
>
<MultistepJsonForm
formData={formState}
fields={customFields}
fields={components}
onChange={handleChange}
onReset={handleFormReset}
onFinish={handleCreate}
@@ -218,7 +221,7 @@ export const TemplatePage = () => {
return {
...step,
validate: createValidator(step.schema),
validate: createValidator(step.schema, validation),
};
})}
/>
+23 -24
View File
@@ -19,8 +19,9 @@ import {
ComponentLoader,
createReactExtension,
getComponentData,
attachComponentData,
} from '@backstage/core';
import { FieldValidation } from '@rjsf/core';
import { FieldValidation, Field } from '@rjsf/core';
import React from 'react';
import { useMount } from 'react-use';
@@ -31,39 +32,37 @@ import {
createCollector,
} from '@backstage/core-api/src/extensions/traversal';
export type FieldExtensionOptions<T> = {
export type FieldExtensionOptions = {
name: string;
component: ComponentLoader<T>;
component: Field;
validation: (data: JsonValue, field: FieldValidation) => void;
};
export function createScaffolderFieldExtension<
T extends (props: any) => JSX.Element | null
>(options: FieldExtensionOptions<T>): Extension<T> {
const componentInData =
'lazy' in options.component
? React.lazy(() =>
options.component.lazy().then(component => ({ default: component })),
)
: options.component.sync;
export function createScaffolderFieldExtension(
options: FieldExtensionOptions,
): Extension<Field> {
const extensionData = {
'scaffolder.extensions.field.v1': options,
};
return createReactExtension({
data: {
'scaffolder.extensions.field.v1': {
...options,
component: componentInData,
},
const Result = (props: any) => <options.component {...props} />;
for (const [key, value] of Object.entries(extensionData)) {
attachComponentData(Result, key, value);
}
return {
expose() {
return Result;
},
component: options.component,
});
};
}
export type ExtensionState = {
fields: FieldExtensionOptions<unknown>[];
fields: FieldExtensionOptions[];
};
export type RegisterFieldExtensionAction = {
type: 'fields';
data: FieldExtensionOptions<unknown>[];
data: FieldExtensionOptions[];
};
export type ExtensionAction = RegisterFieldExtensionAction;
@@ -101,9 +100,9 @@ export const ExtensionCollector = ({
discoverers: [childDiscoverer, routeElementDiscoverer],
collectors: {
fields: createCollector(
() => [] as FieldExtensionOptions<unknown>[],
() => [] as FieldExtensionOptions[],
(acc, node) => {
const data = getComponentData<FieldExtensionOptions<unknown>>(
const data = getComponentData<FieldExtensionOptions>(
node,
'scaffolder.extensions.field.v1',
);
+2 -6
View File
@@ -54,9 +54,7 @@ export const scaffolderPlugin = createPlugin({
export const RepoUrlPicker = scaffolderPlugin.provide(
createScaffolderFieldExtension({
// TODO: work out how to type this component part so we can enforce FieldComponent from RJSF
component: {
sync: RepoUrlPickerComponent,
},
component: RepoUrlPickerComponent,
name: 'RepoUrlPicker',
validation: (value: JsonValue, validation) => {
try {
@@ -73,9 +71,7 @@ export const RepoUrlPicker = scaffolderPlugin.provide(
export const OwnerPicker = scaffolderPlugin.provide(
createScaffolderFieldExtension({
component: {
sync: OwnerPickerComponent,
},
component: OwnerPickerComponent,
name: 'OwnerPicker',
validation: () => {},
}),