chore: initial approach for field extensions

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2024-09-23 14:28:52 +02:00
parent b304f78a31
commit bf6eaf35c8
78 changed files with 924 additions and 268 deletions
+1 -1
View File
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { createApiRef } from '@backstage/core-plugin-api';
import { createApiRef } from '@backstage/frontend-plugin-api';
import { ScaffolderApi } from './types';
/** @public */
+1
View File
@@ -21,3 +21,4 @@ export * from './secrets';
export * from './api';
export * from './hooks';
export * from './layouts';
export * from './utils';
@@ -0,0 +1,56 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
createExtensionBlueprint,
createExtensionDataRef,
} from '@backstage/frontend-plugin-api';
import { z } from 'zod';
import { OpaqueFormField, FormField } from '@internal/scaffolder';
import { FormFieldExtensionData } from './types';
const formFieldExtensionDataRef = createExtensionDataRef<
() => Promise<FormField>
>().with({
id: 'scaffolder.form-field-loader',
});
/**
* @alpha
* Creates extensions that are Field Extensions for the Scaffolder
* */
export const FormFieldBlueprint = createExtensionBlueprint({
kind: 'scaffolder-form-field',
attachTo: { id: 'api:scaffolder/form-fields', input: 'formFields' },
dataRefs: {
formFieldLoader: formFieldExtensionDataRef,
},
output: [formFieldExtensionDataRef],
*factory(params: { field: () => Promise<FormField> }) {
yield formFieldExtensionDataRef(params.field);
},
});
/**
* @alpha
* Used to create a form field binding with typechecking for compliance
*/
export function createFormField<
TReturnValue extends z.ZodType,
TUiOptions extends z.ZodType,
>(opts: FormFieldExtensionData<TReturnValue, TUiOptions>): FormField {
return OpaqueFormField.createInstance('v1', opts);
}
@@ -0,0 +1,18 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './FormFieldBlueprint';
export * from './types';
@@ -0,0 +1,40 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import {
CustomFieldValidator,
FieldExtensionComponentProps,
FieldSchema,
} from '@backstage/plugin-scaffolder-react';
/** @alpha */
export type FormFieldExtensionData<
TReturnValue extends z.ZodType = z.ZodType,
TUiOptions extends z.ZodType = z.ZodType,
> = {
name: string;
component: (
props: FieldExtensionComponentProps<
z.output<TReturnValue>,
z.output<TUiOptions>
>,
) => JSX.Element | null;
validation?: CustomFieldValidator<
z.output<TReturnValue>,
z.output<TUiOptions>
>;
schema?: FieldSchema<z.output<TReturnValue>, z.output<TUiOptions>>;
};
@@ -17,3 +17,4 @@ export * from './components';
export * from './lib';
export * from './hooks';
export * from './overridableComponents';
export * from './blueprints';
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import zodToJsonSchema from 'zod-to-json-schema';
import { JSONSchema7 } from 'json-schema';
import { z } from 'zod';
import {
CustomFieldExtensionSchema,
FieldExtensionComponentProps,
} from './extensions';
/** @public */
export function makeFieldSchema<
TReturnType extends z.ZodType,
TUiOptions extends z.ZodType,
>(options: {
output: (zImpl: typeof z) => TReturnType;
uiOptions?: (zImpl: typeof z) => TUiOptions;
}): FieldSchema<z.output<TReturnType>, z.output<TUiOptions>> {
const { output, uiOptions } = options;
return {
TProps: undefined as any,
schema: {
returnValue: zodToJsonSchema(output(z)) as JSONSchema7,
uiOptions: uiOptions && (zodToJsonSchema(uiOptions(z)) as JSONSchema7),
},
// These will be removed - just here for backwards compat whilst we're moving across
type: undefined as any,
uiOptionsType: undefined as any,
};
}
/**
* @public
* FieldSchema encapsulates a JSONSchema7 along with the
* matching FieldExtensionComponentProps type for a field extension.
*/
export interface FieldSchema<TReturn, TUiOptions> {
/** @deprecated use TProps instead */
readonly type: FieldExtensionComponentProps<TReturn, TUiOptions>;
/** @deprecated will be removed */
readonly uiOptionsType: TUiOptions;
readonly schema: CustomFieldExtensionSchema;
readonly TProps: FieldExtensionComponentProps<TReturn, TUiOptions>;
}