chore: simplify logic a little more and use getComponentData
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -45,8 +45,8 @@ import { NewRelicPage } from '@backstage/plugin-newrelic';
|
||||
import {
|
||||
ScaffolderPage,
|
||||
scaffolderPlugin,
|
||||
OwnerPicker,
|
||||
RepoUrlPicker,
|
||||
OwnerPickerFieldExtension,
|
||||
RepoUrlPickerFieldExtension,
|
||||
} from '@backstage/plugin-scaffolder';
|
||||
import { SearchPage, SearchPageNext } from '@backstage/plugin-search';
|
||||
import { TechRadarPage } from '@backstage/plugin-tech-radar';
|
||||
@@ -112,15 +112,10 @@ const routes = (
|
||||
</Route>
|
||||
<Route path="/catalog-import" element={<CatalogImportPage />} />
|
||||
<Route path="/docs" element={<TechdocsPage />} />
|
||||
<Route
|
||||
path="/create"
|
||||
element={
|
||||
<ScaffolderPage>
|
||||
<RepoUrlPicker />
|
||||
<OwnerPicker />
|
||||
</ScaffolderPage>
|
||||
}
|
||||
/>
|
||||
<Route path="/create" element={<ScaffolderPage />}>
|
||||
<RepoUrlPickerFieldExtension />
|
||||
<OwnerPickerFieldExtension />
|
||||
</Route>
|
||||
<Route path="/explore" element={<ExplorePage />} />
|
||||
<Route
|
||||
path="/tech-radar"
|
||||
|
||||
@@ -82,13 +82,6 @@ export interface ScaffolderApi {
|
||||
// Returns a list of all installed actions.
|
||||
listActions(): Promise<ListActionsResponse>;
|
||||
|
||||
// Register Custom Fields
|
||||
registerCustomField(field: CustomField): void;
|
||||
getCustomFields(): {
|
||||
components: Map<string, Field>;
|
||||
validators: Map<string, (data: JsonValue, field: FieldValidation) => void>;
|
||||
};
|
||||
|
||||
streamLogs({
|
||||
taskId,
|
||||
after,
|
||||
@@ -102,7 +95,6 @@ export class ScaffolderClient implements ScaffolderApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly identityApi: IdentityApi;
|
||||
private readonly scmIntegrationsApi: ScmIntegrationRegistry;
|
||||
private readonly customFields: Map<string, CustomField> = new Map();
|
||||
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
@@ -151,24 +143,6 @@ export class ScaffolderClient implements ScaffolderApi {
|
||||
return schema;
|
||||
}
|
||||
|
||||
registerCustomField(field: CustomField) {
|
||||
this.customFields.set(field.name, field);
|
||||
}
|
||||
|
||||
getCustomFields() {
|
||||
return [...this.customFields.entries()].reduce(
|
||||
(previous, [name, field]) => {
|
||||
previous.components.set(name, field.component);
|
||||
previous.validators.set(name, field.validation);
|
||||
return previous;
|
||||
},
|
||||
{
|
||||
components: new Map(),
|
||||
validators: new Map(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the scaffolding of a component, given a template and its
|
||||
* parameter values.
|
||||
|
||||
@@ -15,21 +15,38 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Routes, Route } from 'react-router';
|
||||
import { Routes, Route, useOutlet } from 'react-router';
|
||||
import { ScaffolderPage } from './ScaffolderPage';
|
||||
import { TemplatePage } from './TemplatePage';
|
||||
import { TaskPage } from './TaskPage';
|
||||
import { ActionsPage } from './ActionsPage';
|
||||
import { getComponentData } from '@backstage/core';
|
||||
import { FieldExtensionOptions } from '../extensions';
|
||||
|
||||
export const Router = () => {
|
||||
const children = useOutlet();
|
||||
const fieldExtensions = React.Children.map(children ?? [], child => {
|
||||
if (!React.isValidElement(child)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = getComponentData<FieldExtensionOptions<unknown>>(
|
||||
child,
|
||||
'scaffolder.extensions.field.v1',
|
||||
);
|
||||
|
||||
return data;
|
||||
}).filter(Boolean);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Routes>
|
||||
<Route path="/" element={<ScaffolderPage />} />
|
||||
<Route path="/templates/:templateName" element={<TemplatePage />} />
|
||||
<Route path="/tasks/:taskId" element={<TaskPage />} />
|
||||
<Route path="/actions" element={<ActionsPage />} />
|
||||
</Routes>
|
||||
</>
|
||||
<Routes>
|
||||
<Route path="/" element={<ScaffolderPage />} />
|
||||
<Route
|
||||
path="/templates/:templateName"
|
||||
element={<TemplatePage customFieldExtensions={fieldExtensions} />}
|
||||
/>
|
||||
<Route path="/tasks/:taskId" element={<TaskPage />} />
|
||||
<Route path="/actions" element={<ActionsPage />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -34,6 +34,7 @@ import { scaffolderApiRef } from '../../api';
|
||||
import { rootRouteRef } from '../../routes';
|
||||
import { MultistepJsonForm } from '../MultistepJsonForm';
|
||||
import { JsonObject, JsonValue } from '@backstage/config';
|
||||
import { FieldExtensionOptions } from '../../extensions';
|
||||
|
||||
const useTemplateParameterSchema = (templateName: string) => {
|
||||
const scaffolderApi = useApi(scaffolderApiRef);
|
||||
@@ -132,7 +133,11 @@ const storePathValidator = (
|
||||
return errors;
|
||||
};
|
||||
|
||||
export const TemplatePage = () => {
|
||||
export const TemplatePage = ({
|
||||
customFieldExtensions = [],
|
||||
}: {
|
||||
customFieldExtensions: FieldExtensionOptions[];
|
||||
}) => {
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const scaffolderApi = useApi(scaffolderApiRef);
|
||||
const { templateName } = useParams();
|
||||
@@ -141,7 +146,6 @@ export const TemplatePage = () => {
|
||||
const { schema, loading, error } = useTemplateParameterSchema(templateName);
|
||||
const [formState, setFormState] = useState({});
|
||||
const handleFormReset = () => setFormState({});
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: IChangeEvent) => setFormState(e.formData),
|
||||
[setFormState],
|
||||
@@ -166,7 +170,14 @@ export const TemplatePage = () => {
|
||||
return <Navigate to={rootLink()} />;
|
||||
}
|
||||
|
||||
const { components, validators } = scaffolderApi.getCustomFields();
|
||||
const customFieldComponents = Object.fromEntries(
|
||||
customFieldExtensions.map(({ name, component }) => [name, component]),
|
||||
);
|
||||
|
||||
const customFieldValidators = Object.fromEntries(
|
||||
customFieldExtensions.map(({ name, validation }) => [name, validation]),
|
||||
);
|
||||
|
||||
return (
|
||||
<Page themeId="home">
|
||||
<Header
|
||||
@@ -188,7 +199,7 @@ export const TemplatePage = () => {
|
||||
>
|
||||
<MultistepJsonForm
|
||||
formData={formState}
|
||||
fields={Object.fromEntries(components.entries())}
|
||||
fields={customFieldComponents}
|
||||
onChange={handleChange}
|
||||
onReset={handleFormReset}
|
||||
onFinish={handleCreate}
|
||||
@@ -204,7 +215,7 @@ export const TemplatePage = () => {
|
||||
|
||||
return {
|
||||
...step,
|
||||
validate: createValidator(step.schema, validators),
|
||||
validate: createValidator(step.schema, customFieldValidators),
|
||||
};
|
||||
})}
|
||||
/>
|
||||
|
||||
@@ -24,7 +24,7 @@ export type FieldExtensionOptions<T = any> = {
|
||||
|
||||
export function createScaffolderFieldExtension<T = any>(
|
||||
options: FieldExtensionOptions<T>,
|
||||
): Extension<void> {
|
||||
): Extension<() => null> {
|
||||
return {
|
||||
expose() {
|
||||
const FieldExtensionDataHolder: any = () => null;
|
||||
|
||||
@@ -18,8 +18,8 @@ export {
|
||||
scaffolderPlugin,
|
||||
scaffolderPlugin as plugin,
|
||||
ScaffolderPage,
|
||||
OwnerPicker,
|
||||
RepoUrlPicker,
|
||||
OwnerPickerFieldExtension,
|
||||
RepoUrlPickerFieldExtension,
|
||||
} from './plugin';
|
||||
export type { ScaffolderApi } from './api';
|
||||
export { ScaffolderClient, scaffolderApiRef } from './api';
|
||||
|
||||
Reference in New Issue
Block a user