Merge pull request #26674 from backstage/mob/scaffolder-nfs

NFS: Investigate `FormFieldBlueprint` to create `FormFieldExtensions` in the Scaffolder Plugin
This commit is contained in:
Ben Lambert
2024-09-24 10:33:29 +02:00
committed by GitHub
78 changed files with 924 additions and 268 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder-react': minor
'@backstage/plugin-scaffolder': minor
---
Added support for `FormFieldBlueprint` to create field extensions in the Scaffolder plugin
@@ -0,0 +1,5 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
rules: {
'@backstage/no-top-level-material-ui-4-imports': 'error',
},
});
+3
View File
@@ -0,0 +1,3 @@
# @internal/scaffolder
This is an internal package used by the other scaffolder packages. It does not get published to NPM, but instead inlined into consuming packages due to the `backstage.inline` flag in `package.json`.
@@ -0,0 +1,9 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: internal-scaffolder
title: '@internal/scaffolder'
spec:
lifecycle: experimental
type: backstage-web-library
owner: maintainers
+32
View File
@@ -0,0 +1,32 @@
{
"name": "@internal/scaffolder",
"version": "0.0.1",
"backstage": {
"role": "web-library",
"inline": true
},
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "packages/scaffolder-internal"
},
"license": "Apache-2.0",
"sideEffects": false,
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"lint": "backstage-cli package lint",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/plugin-scaffolder-react": "workspace:^",
"zod": "^3.22.4"
},
"devDependencies": {
"@backstage/cli": "workspace:^"
}
}
+17
View File
@@ -0,0 +1,17 @@
/*
* 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 './wiring';
@@ -0,0 +1,33 @@
/*
* 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 { OpaqueType } from '@internal/opaque';
import { z } from 'zod';
import { FormFieldExtensionData } from '@backstage/plugin-scaffolder-react/alpha';
/** @alpha */
export interface FormField {
readonly $$type: '@backstage/scaffolder/FormField';
}
/** @alpha */
export const OpaqueFormField = OpaqueType.create<{
public: FormField;
versions: FormFieldExtensionData<z.ZodType, z.ZodType> & {
readonly version: 'v1';
};
}>({ type: '@backstage/scaffolder/FormField', versions: ['v1'] });
@@ -0,0 +1,16 @@
/*
* 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 { OpaqueFormField, type FormField } from './InternalFormField';
+1
View File
@@ -62,6 +62,7 @@
"@backstage/catalog-model": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
"@backstage/plugin-permission-react": "workspace:^",
"@backstage/plugin-scaffolder-common": "workspace:^",
@@ -7,10 +7,15 @@
import { ApiHolder } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { CustomFieldValidator } from '@backstage/plugin-scaffolder-react';
import { Dispatch } from 'react';
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
import { FieldExtensionComponentProps } from '@backstage/plugin-scaffolder-react';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { FieldSchema } from '@backstage/plugin-scaffolder-react';
import { FieldValidation } from '@rjsf/utils';
import { FormField } from '@internal/scaffolder';
import { FormProps } from '@backstage/plugin-scaffolder-react';
import { IconComponent } from '@backstage/core-plugin-api';
import { JsonObject } from '@backstage/types';
@@ -34,6 +39,7 @@ import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';
import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common';
import { UiSchema } from '@rjsf/utils';
import { WidgetProps } from '@rjsf/utils';
import { z } from 'zod';
// @alpha (undocumented)
export type BackstageOverrides = Overrides & {
@@ -57,6 +63,12 @@ export const createAsyncValidators: (
// @alpha
export const createFieldValidation: () => FieldValidation;
// @alpha
export function createFormField<
TReturnValue extends z.ZodType,
TUiOptions extends z.ZodType,
>(opts: FormFieldExtensionData<TReturnValue, TUiOptions>): FormField;
// @alpha
export const DefaultTemplateOutputs: (props: {
output?: ScaffolderTaskOutput;
@@ -76,6 +88,49 @@ export const Form: (
props: PropsWithChildren<ScaffolderRJSFFormProps>,
) => React_2.JSX.Element;
// @alpha
export const FormFieldBlueprint: ExtensionBlueprint<{
kind: 'scaffolder-form-field';
name: undefined;
params: {
field: () => Promise<FormField>;
};
output: ConfigurableExtensionDataRef<
() => Promise<FormField>,
'scaffolder.form-field-loader',
{}
>;
inputs: {};
config: {};
configInput: {};
dataRefs: {
formFieldLoader: ConfigurableExtensionDataRef<
() => Promise<FormField>,
'scaffolder.form-field-loader',
{}
>;
};
}>;
// @alpha (undocumented)
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>>;
};
// @alpha (undocumented)
export type FormValidation = {
[name: string]: FieldValidation | FormValidation;
@@ -320,6 +375,7 @@ export type WorkflowProps = {
// Warnings were encountered during analysis:
//
// src/next/blueprints/types.d.ts:5:1 - (ae-undocumented) Missing documentation for "FormFieldExtensionData".
// src/next/components/ScaffolderField/ScaffolderField.d.ts:7:5 - (ae-undocumented) Missing documentation for "rawDescription".
// src/next/components/ScaffolderField/ScaffolderField.d.ts:8:5 - (ae-undocumented) Missing documentation for "errors".
// src/next/components/ScaffolderField/ScaffolderField.d.ts:9:5 - (ae-undocumented) Missing documentation for "rawErrors".
+28 -1
View File
@@ -6,7 +6,7 @@
/// <reference types="react" />
import { ApiHolder } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/frontend-plugin-api';
import { ComponentType } from 'react';
import { CustomValidator } from '@rjsf/utils';
import { ElementType } from 'react';
@@ -44,6 +44,7 @@ import { TemplatesType } from '@rjsf/utils';
import { UIOptionsType } from '@rjsf/utils';
import { UiSchema } from '@rjsf/utils';
import { ValidatorType } from '@rjsf/utils';
import { z } from 'zod';
// @public
export type Action = {
@@ -125,6 +126,18 @@ export interface FieldExtensionUiSchema<TFieldReturnValue, TUiOptions>
'ui:options'?: TUiOptions & UIOptionsType<TFieldReturnValue>;
}
// @public
export interface FieldSchema<TReturn, TUiOptions> {
// (undocumented)
readonly schema: CustomFieldExtensionSchema;
// (undocumented)
readonly TProps: FieldExtensionComponentProps<TReturn, TUiOptions>;
// @deprecated (undocumented)
readonly type: FieldExtensionComponentProps<TReturn, TUiOptions>;
// @deprecated (undocumented)
readonly uiOptionsType: TUiOptions;
}
// @public
export type FormProps = Pick<
FormProps_2,
@@ -168,6 +181,15 @@ export type LogEvent = {
taskId: string;
};
// @public (undocumented)
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>>;
// @public
export type ReviewStepProps = {
disableButtons: boolean;
@@ -566,6 +588,11 @@ export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets;
// src/layouts/createScaffolderLayout.d.ts:17:5 - (ae-undocumented) Missing documentation for "component".
// src/secrets/SecretsContext.d.ts:14:5 - (ae-undocumented) Missing documentation for "setSecrets".
// src/secrets/SecretsContext.d.ts:15:5 - (ae-undocumented) Missing documentation for "secrets".
// src/utils.d.ts:4:1 - (ae-undocumented) Missing documentation for "makeFieldSchema".
// src/utils.d.ts:15:5 - (ae-undocumented) Missing documentation for "type".
// src/utils.d.ts:17:5 - (ae-undocumented) Missing documentation for "uiOptionsType".
// src/utils.d.ts:18:5 - (ae-undocumented) Missing documentation for "schema".
// src/utils.d.ts:19:5 - (ae-undocumented) Missing documentation for "TProps".
// (No @packageDocumentation comment for this package)
```
+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>;
}
+2 -2
View File
@@ -30,7 +30,7 @@
"sideEffects": false,
"exports": {
".": "./src/index.ts",
"./alpha": "./src/alpha.tsx",
"./alpha": "./src/alpha/index.ts",
"./package.json": "./package.json"
},
"main": "src/index.ts",
@@ -38,7 +38,7 @@
"typesVersions": {
"*": {
"alpha": [
"src/alpha.tsx"
"src/alpha/index.ts"
],
"package.json": [
"package.json"
+54 -11
View File
@@ -3,16 +3,21 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="react" />
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { FormField } from '@internal/scaffolder';
import type { FormProps as FormProps_2 } from '@rjsf/core';
import { FormProps as FormProps_3 } from '@backstage/plugin-scaffolder-react';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react';
import { LayoutOptions } from '@backstage/plugin-scaffolder-react';
import { PathParams } from '@backstage/core-plugin-api';
import { default as React_2 } from 'react';
@@ -69,11 +74,7 @@ const _default: FrontendPlugin<
path?: string | undefined;
};
output:
| ConfigurableExtensionDataRef<
React_2.JSX.Element,
'core.reactElement',
{}
>
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef<AnyRouteRefParams>,
@@ -110,6 +111,48 @@ const _default: FrontendPlugin<
routeRef: RouteRef<undefined>;
};
}>;
'scaffolder-form-field:scaffolder/repo-url-picker': ExtensionDefinition<{
kind: 'scaffolder-form-field';
name: 'repo-url-picker';
config: {};
configInput: {};
output: ConfigurableExtensionDataRef<
() => Promise<FormField>,
'scaffolder.form-field-loader',
{}
>;
inputs: {};
params: {
field: () => Promise<FormField>;
};
}>;
'api:scaffolder/form-fields': ExtensionDefinition<{
config: {};
configInput: {};
output: ConfigurableExtensionDataRef<
AnyApiFactory,
'core.api.factory',
{}
>;
inputs: {
formFields: ExtensionInput<
ConfigurableExtensionDataRef<
() => Promise<FormField>,
'scaffolder.form-field-loader',
{}
>,
{
singleton: false;
optional: false;
}
>;
};
kind: 'api';
name: 'form-fields';
params: {
factory: AnyApiFactory;
};
}>;
}
>;
export default _default;
@@ -315,12 +358,12 @@ export type TemplateWizardPageProps = {
// Warnings were encountered during analysis:
//
// src/alpha.d.ts:5:15 - (ae-undocumented) Missing documentation for "_default".
// src/next/TemplateEditorPage/CustomFieldExplorer.d.ts:4:1 - (ae-undocumented) Missing documentation for "ScaffolderCustomFieldExplorerClassKey".
// src/next/TemplateEditorPage/TemplateEditor.d.ts:6:1 - (ae-undocumented) Missing documentation for "ScaffolderTemplateEditorClassKey".
// src/next/TemplateEditorPage/TemplateFormPreviewer.d.ts:4:1 - (ae-undocumented) Missing documentation for "ScaffolderTemplateFormPreviewerClassKey".
// src/next/TemplateListPage/TemplateListPage.d.ts:7:1 - (ae-undocumented) Missing documentation for "TemplateListPageProps".
// src/next/TemplateWizardPage/TemplateWizardPage.d.ts:6:1 - (ae-undocumented) Missing documentation for "TemplateWizardPageProps".
// src/alpha/components/TemplateEditorPage/CustomFieldExplorer.d.ts:4:1 - (ae-undocumented) Missing documentation for "ScaffolderCustomFieldExplorerClassKey".
// src/alpha/components/TemplateEditorPage/TemplateEditor.d.ts:6:1 - (ae-undocumented) Missing documentation for "ScaffolderTemplateEditorClassKey".
// src/alpha/components/TemplateEditorPage/TemplateFormPreviewer.d.ts:4:1 - (ae-undocumented) Missing documentation for "ScaffolderTemplateFormPreviewerClassKey".
// src/alpha/components/TemplateListPage/TemplateListPage.d.ts:7:1 - (ae-undocumented) Missing documentation for "TemplateListPageProps".
// src/alpha/components/TemplateWizardPage/TemplateWizardPage.d.ts:6:1 - (ae-undocumented) Missing documentation for "TemplateWizardPageProps".
// src/alpha/plugin.d.ts:3:15 - (ae-undocumented) Missing documentation for "_default".
// src/translation.d.ts:2:22 - (ae-undocumented) Missing documentation for "scaffolderTranslationRef".
// (No @packageDocumentation comment for this package)
+8 -15
View File
@@ -19,6 +19,7 @@ import { FetchApi } from '@backstage/core-plugin-api';
import { FieldExtensionComponent as FieldExtensionComponent_2 } from '@backstage/plugin-scaffolder-react';
import { FieldExtensionComponentProps as FieldExtensionComponentProps_2 } from '@backstage/plugin-scaffolder-react';
import { FieldExtensionOptions as FieldExtensionOptions_2 } from '@backstage/plugin-scaffolder-react';
import { FieldSchema as FieldSchema_2 } from '@backstage/plugin-scaffolder-react';
import { FieldValidation } from '@rjsf/utils';
import { FormProps } from '@backstage/plugin-scaffolder-react';
import { IdentityApi } from '@backstage/core-plugin-api';
@@ -174,15 +175,8 @@ export type FieldExtensionComponentProps<
// @public @deprecated (undocumented)
export type FieldExtensionOptions = FieldExtensionOptions_2;
// @public
export interface FieldSchema<TReturn, TUiOptions> {
// (undocumented)
readonly schema: CustomFieldExtensionSchema_2;
// (undocumented)
readonly type: FieldExtensionComponentProps_2<TReturn, TUiOptions>;
// (undocumented)
readonly uiOptionsType: TUiOptions;
}
// @public @deprecated (undocumented)
export interface FieldSchema<T, P> extends FieldSchema_2<T, P> {}
// @public @deprecated (undocumented)
export type LayoutOptions = LayoutOptions_2;
@@ -196,7 +190,7 @@ export type ListActionsResponse = ListActionsResponse_2;
// @public @deprecated (undocumented)
export type LogEvent = LogEvent_2;
// @public
// @public @deprecated (undocumented)
export function makeFieldSchemaFromZod<
TReturnSchema extends z.ZodType,
TUiOptionsSchema extends z.ZodType = z.ZodType<any, any, {}>,
@@ -443,7 +437,7 @@ export const RepoUrlPickerFieldExtension: FieldExtensionComponent_2<
>;
// @public (undocumented)
export const RepoUrlPickerFieldSchema: FieldSchema<
export const RepoUrlPickerFieldSchema: FieldSchema_2<
string,
{
allowedHosts?: string[] | undefined;
@@ -469,7 +463,7 @@ export const RepoUrlPickerFieldSchema: FieldSchema<
}
>;
// @public
// @public @deprecated
export type RepoUrlPickerUiOptions =
typeof RepoUrlPickerFieldSchema.uiOptionsType;
@@ -689,9 +683,8 @@ export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets_2;
// src/components/fields/OwnedEntityPicker/schema.d.ts:4:22 - (ae-undocumented) Missing documentation for "OwnedEntityPickerFieldSchema".
// src/components/fields/OwnerPicker/schema.d.ts:4:22 - (ae-undocumented) Missing documentation for "OwnerPickerFieldSchema".
// src/components/fields/RepoUrlPicker/schema.d.ts:4:22 - (ae-undocumented) Missing documentation for "RepoUrlPickerFieldSchema".
// src/components/fields/utils.d.ts:9:5 - (ae-undocumented) Missing documentation for "schema".
// src/components/fields/utils.d.ts:10:5 - (ae-undocumented) Missing documentation for "type".
// src/components/fields/utils.d.ts:11:5 - (ae-undocumented) Missing documentation for "uiOptionsType".
// src/components/fields/utils.d.ts:7:1 - (ae-undocumented) Missing documentation for "FieldSchema".
// src/components/fields/utils.d.ts:15:1 - (ae-undocumented) Missing documentation for "makeFieldSchemaFromZod".
// src/deprecated.d.ts:7:22 - (ae-undocumented) Missing documentation for "rootRouteRef".
// src/deprecated.d.ts:12:22 - (ae-undocumented) Missing documentation for "createScaffolderFieldExtension".
// src/deprecated.d.ts:17:22 - (ae-undocumented) Missing documentation for "ScaffolderFieldExtensions".
-113
View File
@@ -1,113 +0,0 @@
/*
* Copyright 2023 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 React from 'react';
import {
createApiFactory,
createFrontendPlugin,
discoveryApiRef,
fetchApiRef,
identityApiRef,
ApiBlueprint,
PageBlueprint,
NavItemBlueprint,
} from '@backstage/frontend-plugin-api';
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
import {
compatWrapper,
convertLegacyRouteRef,
convertLegacyRouteRefs,
} from '@backstage/core-compat-api';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import { ScaffolderClient } from './api';
import {
registerComponentRouteRef,
rootRouteRef,
viewTechDocRouteRef,
selectedTemplateRouteRef,
scaffolderTaskRouteRef,
scaffolderListTaskRouteRef,
actionsRouteRef,
editRouteRef,
} from './routes';
export {
type FormProps,
type TemplateListPageProps,
type TemplateWizardPageProps,
type ScaffolderCustomFieldExplorerClassKey,
type ScaffolderTemplateEditorClassKey,
type ScaffolderTemplateFormPreviewerClassKey,
} from './next';
export { scaffolderTranslationRef } from './translation';
const scaffolderApi = ApiBlueprint.make({
params: {
factory: createApiFactory({
api: scaffolderApiRef,
deps: {
discoveryApi: discoveryApiRef,
scmIntegrationsApi: scmIntegrationsApiRef,
fetchApi: fetchApiRef,
identityApi: identityApiRef,
},
factory: ({ discoveryApi, scmIntegrationsApi, fetchApi, identityApi }) =>
new ScaffolderClient({
discoveryApi,
scmIntegrationsApi,
fetchApi,
identityApi,
}),
}),
},
});
const scaffolderPage = PageBlueprint.make({
params: {
routeRef: convertLegacyRouteRef(rootRouteRef),
defaultPath: '/create',
loader: () =>
import('./components/Router').then(m => compatWrapper(<m.Router />)),
},
});
const scaffolderNavItem = NavItemBlueprint.make({
params: {
routeRef: convertLegacyRouteRef(rootRouteRef),
title: 'Create...',
icon: CreateComponentIcon,
},
});
/** @alpha */
export default createFrontendPlugin({
id: 'scaffolder',
routes: convertLegacyRouteRefs({
root: rootRouteRef,
selectedTemplate: selectedTemplateRouteRef,
ongoingTask: scaffolderTaskRouteRef,
actions: actionsRouteRef,
listTasks: scaffolderListTaskRouteRef,
edit: editRouteRef,
}),
externalRoutes: convertLegacyRouteRefs({
registerComponent: registerComponentRouteRef,
viewTechDoc: viewTechDocRouteRef,
}),
extensions: [scaffolderApi, scaffolderPage, scaffolderNavItem],
});
+46
View File
@@ -0,0 +1,46 @@
/*
* 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 {
ApiBlueprint,
createApiFactory,
discoveryApiRef,
fetchApiRef,
identityApiRef,
} from '@backstage/frontend-plugin-api';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
import { ScaffolderClient } from '../api';
export const scaffolderApi = ApiBlueprint.make({
params: {
factory: createApiFactory({
api: scaffolderApiRef,
deps: {
discoveryApi: discoveryApiRef,
scmIntegrationsApi: scmIntegrationsApiRef,
fetchApi: fetchApiRef,
identityApi: identityApiRef,
},
factory: ({ discoveryApi, scmIntegrationsApi, fetchApi, identityApi }) =>
new ScaffolderClient({
discoveryApi,
scmIntegrationsApi,
fetchApi,
identityApi,
}),
}),
},
});
@@ -0,0 +1,63 @@
/*
* 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 {
ApiBlueprint,
createApiFactory,
createExtensionInput,
} from '@backstage/frontend-plugin-api';
import { formFieldsApiRef } from './ref';
import { ScaffolderFormFieldsApi } from './types';
import { FormFieldBlueprint } from '@backstage/plugin-scaffolder-react/alpha';
import { FormField, OpaqueFormField } from '@internal/scaffolder';
class DefaultScaffolderFormFieldsApi implements ScaffolderFormFieldsApi {
constructor(
private readonly formFieldLoaders: Array<() => Promise<FormField>> = [],
) {}
async getFormFields() {
const formFields = await Promise.all(
this.formFieldLoaders.map(loader => loader()),
);
const internalFormFields = formFields.map(OpaqueFormField.toInternal);
return internalFormFields;
}
}
export const formFieldsApi = ApiBlueprint.makeWithOverrides({
name: 'form-fields',
inputs: {
formFields: createExtensionInput([
FormFieldBlueprint.dataRefs.formFieldLoader,
]),
},
factory(originalFactory, { inputs }) {
const formFieldLoaders = inputs.formFields.map(e =>
e.get(FormFieldBlueprint.dataRefs.formFieldLoader),
);
return originalFactory({
factory: createApiFactory({
api: formFieldsApiRef,
deps: {},
factory: () => new DefaultScaffolderFormFieldsApi(formFieldLoaders),
}),
});
},
});
+18
View File
@@ -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 { formFieldsApiRef } from './ref';
export type { ScaffolderFormFieldsApi } from './types';
+22
View File
@@ -0,0 +1,22 @@
/*
* 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 { createApiRef } from '@backstage/frontend-plugin-api';
import { ScaffolderFormFieldsApi } from './types';
export const formFieldsApiRef = createApiRef<ScaffolderFormFieldsApi>({
id: 'plugin.scaffolder.form-fields',
});
+21
View File
@@ -0,0 +1,21 @@
/*
* 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 { FormFieldExtensionData } from '@backstage/plugin-scaffolder-react/alpha';
export interface ScaffolderFormFieldsApi {
getFormFields(): Promise<FormFieldExtensionData[]>;
}
@@ -34,7 +34,7 @@ import { TemplateEditorForm } from './TemplateEditorForm';
import validator from '@rjsf/validator-ajv8';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
/** @public */
export type ScaffolderCustomFieldExplorerClassKey =
@@ -37,7 +37,7 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { Form } from '@backstage/plugin-scaffolder-react/alpha';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
import { TemplateEditorForm } from './TemplateEditorForm';
const useStyles = makeStyles(
@@ -22,8 +22,8 @@ import { useRouteRef } from '@backstage/core-plugin-api';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { editRouteRef } from '../../routes';
import { scaffolderTranslationRef } from '../../translation';
import { editRouteRef } from '../../../routes';
import { scaffolderTranslationRef } from '../../../translation';
import { CustomFieldExplorer } from './CustomFieldExplorer';
@@ -20,7 +20,7 @@ import React, { createContext, ReactNode, useContext, useEffect } from 'react';
import {
TemplateDirectoryAccess,
TemplateFileAccess,
} from '../../lib/filesystem';
} from '../../../lib/filesystem';
const MAX_SIZE = 1024 * 1024;
const MAX_SIZE_MESSAGE = 'This file is too large to be displayed';
@@ -27,7 +27,7 @@ import { useDryRun } from '../DryRunContext';
import { DryRunResultsList } from './DryRunResultsList';
import { DryRunResultsView } from './DryRunResultsView';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../../translation';
import { scaffolderTranslationRef } from '../../../../translation';
const useStyles = makeStyles(theme => ({
header: {
@@ -27,9 +27,9 @@ import DeleteIcon from '@material-ui/icons/Delete';
import DownloadIcon from '@material-ui/icons/GetApp';
import React from 'react';
import { useDryRun } from '../DryRunContext';
import { downloadBlob } from '../../../lib/download';
import { downloadBlob } from '../../../../lib/download';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../../translation';
import { scaffolderTranslationRef } from '../../../../translation';
const useStyles = makeStyles(theme => ({
root: {
@@ -26,11 +26,11 @@ import CodeMirror from '@uiw/react-codemirror';
import React, { useEffect, useMemo, useState } from 'react';
import { useDryRun } from '../DryRunContext';
import { DryRunResultsSplitView } from './DryRunResultsSplitView';
import { FileBrowser } from '../../../components/FileBrowser';
import { FileBrowser } from '../../../../components/FileBrowser';
import { TaskPageLinks } from './TaskPageLinks';
import { TaskStatusStepper } from './TaskStatusStepper';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../../translation';
import { scaffolderTranslationRef } from '../../../../translation';
const useStyles = makeStyles({
root: {
@@ -32,7 +32,7 @@ import useInterval from 'react-use/esm/useInterval';
import humanizeDuration from 'humanize-duration';
import classNames from 'classnames';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../../translation';
import { scaffolderTranslationRef } from '../../../../translation';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -21,7 +21,7 @@ import type {
LayoutOptions,
} from '@backstage/plugin-scaffolder-react';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { TemplateDirectoryAccess } from '../../lib/filesystem';
import { TemplateDirectoryAccess } from '../../../lib/filesystem';
import { DirectoryEditorProvider } from './DirectoryEditorContext';
import { TemplateEditorToolbar } from './TemplateEditorToolbar';
import { TemplateEditorBrowser } from './TemplateEditorBrowser';
@@ -17,7 +17,7 @@ import { renderInTestApp } from '@backstage/test-utils';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { MockFileSystemAccess } from '../../lib/filesystem/MockFileSystemAccess';
import { MockFileSystemAccess } from '../../../lib/filesystem/MockFileSystemAccess';
import { DirectoryEditorProvider } from './DirectoryEditorContext';
import { TemplateEditorBrowser } from './TemplateEditorBrowser';
@@ -23,9 +23,9 @@ import RefreshIcon from '@material-ui/icons/Refresh';
import SaveIcon from '@material-ui/icons/Save';
import React from 'react';
import { useDirectoryEditor } from './DirectoryEditorContext';
import { FileBrowser } from '../../components/FileBrowser';
import { FileBrowser } from '../../../components/FileBrowser';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
const useStyles = makeStyles(
theme => ({
@@ -34,7 +34,8 @@ import {
} from '@backstage/plugin-scaffolder-react/alpha';
import { useDryRun } from './DryRunContext';
import { useDirectoryEditor } from './DirectoryEditorContext';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
const useStyles = makeStyles({
containerWrapper: {
@@ -22,9 +22,9 @@ import Tooltip from '@material-ui/core/Tooltip';
import Typography from '@material-ui/core/Typography';
import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
import { makeStyles } from '@material-ui/core/styles';
import { WebFileSystemAccess } from '../../lib/filesystem';
import { WebFileSystemAccess } from '../../../lib/filesystem';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
const useStyles = makeStyles(theme => ({
introText: {
@@ -15,7 +15,9 @@
*/
import React from 'react';
import { Content, Header, Page } from '@backstage/core-components';
import { WebFileSystemAccess } from '../../lib/filesystem';
import { WebFileSystemAccess } from '../../../lib/filesystem';
import { TemplateEditorIntro } from './TemplateEditorIntro';
import { ScaffolderPageContextMenu } from '@backstage/plugin-scaffolder-react/alpha';
import { useNavigate } from 'react-router-dom';
@@ -27,11 +29,11 @@ import {
rootRouteRef,
scaffolderListTaskRouteRef,
templateFormRouteRef,
} from '../../routes';
} from '../../../routes';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { WebFileSystemStore } from '../../lib/filesystem/WebFileSystemAccess';
import { createExampleTemplate } from '../../lib/filesystem/createExampleTemplate';
import { scaffolderTranslationRef } from '../../../translation';
import { WebFileSystemStore } from '../../../lib/filesystem/WebFileSystemAccess';
import { createExampleTemplate } from '../../../lib/filesystem/createExampleTemplate';
export function TemplateEditorPage() {
const navigate = useNavigate();
@@ -30,7 +30,7 @@ import CodeMirror from '@uiw/react-codemirror';
import React, { useMemo } from 'react';
import { useDirectoryEditor } from './DirectoryEditorContext';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
const useStyles = makeStyles(theme => ({
container: {
@@ -34,7 +34,7 @@ import DescriptionIcon from '@material-ui/icons/Description';
import { Link } from '@backstage/core-components';
import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react';
import { ActionPageContent } from '../../components/ActionsPage/ActionsPage';
import { ActionPageContent } from '../../../components/ActionsPage/ActionsPage';
import { CustomFieldPlaygroud } from './CustomFieldPlaygroud';
const useStyles = makeStyles(
@@ -28,8 +28,8 @@ import {
FieldExtensionOptions,
} from '@backstage/plugin-scaffolder-react';
import { editRouteRef } from '../../routes';
import { scaffolderTranslationRef } from '../../translation';
import { editRouteRef } from '../../../routes';
import { scaffolderTranslationRef } from '../../../translation';
import { TemplateFormPreviewer } from './TemplateFormPreviewer';
@@ -42,7 +42,7 @@ import { TemplateEditorToolbar } from './TemplateEditorToolbar';
import { TemplateEditorForm } from './TemplateEditorForm';
import { TemplateEditorTextArea } from './TemplateEditorTextArea';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
const EXAMPLE_TEMPLATE_PARAMS_YAML = `# Edit the template parameters below to see how they will render in the scaffolder form UI
parameters:
@@ -25,8 +25,11 @@ import {
type LayoutOptions,
} from '@backstage/plugin-scaffolder-react';
import { scaffolderTranslationRef } from '../../translation';
import { WebFileSystemAccess, WebFileSystemStore } from '../../lib/filesystem';
import { scaffolderTranslationRef } from '../../../translation';
import {
WebFileSystemAccess,
WebFileSystemStore,
} from '../../../lib/filesystem';
import { TemplateEditor } from './TemplateEditor';
import { makeStyles } from '@material-ui/core/styles';
@@ -25,7 +25,7 @@ import {
TestApiProvider,
} from '@backstage/test-utils';
import React from 'react';
import { rootRouteRef } from '../../routes';
import { rootRouteRef } from '../../../routes';
import { TemplateListPage } from './TemplateListPage';
describe('TemplateListPage', () => {
@@ -50,14 +50,14 @@ import {
scaffolderListTaskRouteRef,
selectedTemplateRouteRef,
viewTechDocRouteRef,
} from '../../routes';
} from '../../../routes';
import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';
import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react';
import {
TranslationFunction,
useTranslationRef,
} from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
/**
* @alpha
@@ -28,7 +28,7 @@ import {
SecretsContextProvider,
} from '@backstage/plugin-scaffolder-react';
import { TemplateWizardPage } from './TemplateWizardPage';
import { rootRouteRef } from '../../routes';
import { rootRouteRef } from '../../../routes';
import { ANNOTATION_EDIT_URL } from '@backstage/catalog-model';
import { CatalogApi } from '@backstage/catalog-client';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
@@ -44,9 +44,9 @@ import {
rootRouteRef,
scaffolderTaskRouteRef,
selectedTemplateRouteRef,
} from '../../routes';
} from '../../../routes';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
import { TemplateWizardPageContextMenu } from './TemplateWizardPageContextMenu';
@@ -25,7 +25,7 @@ import Edit from '@material-ui/icons/Edit';
import MoreVert from '@material-ui/icons/MoreVert';
import React, { useState } from 'react';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../translation';
import { scaffolderTranslationRef } from '../../../translation';
const useStyles = makeStyles(theme => ({
button: {
@@ -0,0 +1,52 @@
/*
* Copyright 2023 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 {
compatWrapper,
convertLegacyRouteRef,
} from '@backstage/core-compat-api';
import {
NavItemBlueprint,
PageBlueprint,
} from '@backstage/frontend-plugin-api';
import React from 'react';
import { rootRouteRef } from '../routes';
import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
import { FormFieldBlueprint } from '@backstage/plugin-scaffolder-react/alpha';
export const scaffolderPage = PageBlueprint.make({
params: {
routeRef: convertLegacyRouteRef(rootRouteRef),
defaultPath: '/create',
loader: () =>
import('../components/Router').then(m => compatWrapper(<m.Router />)),
},
});
export const scaffolderNavItem = NavItemBlueprint.make({
params: {
routeRef: convertLegacyRouteRef(rootRouteRef),
title: 'Create...',
icon: CreateComponentIcon,
},
});
export const repoUrlPickerFormField = FormFieldBlueprint.make({
name: 'repo-url-picker',
params: {
field: () => import('./fields/RepoUrlPicker').then(m => m.RepoUrlPicker),
},
});
@@ -0,0 +1,28 @@
/*
* 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 { createFormField } from '@backstage/plugin-scaffolder-react/alpha';
import { RepoUrlPicker as Component } from '../../components/fields/RepoUrlPicker/RepoUrlPicker';
import {
RepoUrlPickerFieldSchema,
repoPickerValidation,
} from '../../components';
export const RepoUrlPicker = createFormField({
component: Component,
name: 'RepoUrlPicker',
validation: repoPickerValidation,
schema: RepoUrlPickerFieldSchema,
});
+27
View File
@@ -0,0 +1,27 @@
/*
* 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 {
type FormProps,
type TemplateListPageProps,
type TemplateWizardPageProps,
type ScaffolderCustomFieldExplorerClassKey,
type ScaffolderTemplateEditorClassKey,
type ScaffolderTemplateFormPreviewerClassKey,
} from './components';
export { scaffolderTranslationRef } from '../translation';
export { default } from './plugin';
+59
View File
@@ -0,0 +1,59 @@
/*
* 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 { convertLegacyRouteRefs } from '@backstage/core-compat-api';
import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
import {
rootRouteRef,
actionsRouteRef,
editRouteRef,
registerComponentRouteRef,
scaffolderListTaskRouteRef,
scaffolderTaskRouteRef,
selectedTemplateRouteRef,
viewTechDocRouteRef,
} from '../routes';
import { scaffolderApi } from './api';
import {
repoUrlPickerFormField,
scaffolderNavItem,
scaffolderPage,
} from './extensions';
import { formFieldsApi } from './api/FormFieldsApi';
/** @alpha */
export default createFrontendPlugin({
id: 'scaffolder',
routes: convertLegacyRouteRefs({
root: rootRouteRef,
selectedTemplate: selectedTemplateRouteRef,
ongoingTask: scaffolderTaskRouteRef,
actions: actionsRouteRef,
listTasks: scaffolderListTaskRouteRef,
edit: editRouteRef,
}),
externalRoutes: convertLegacyRouteRefs({
registerComponent: registerComponentRouteRef,
viewTechDoc: viewTechDocRouteRef,
}),
extensions: [
scaffolderApi,
scaffolderPage,
scaffolderNavItem,
formFieldsApi,
repoUrlPickerFormField,
],
});
@@ -25,9 +25,9 @@ import {
createScaffolderLayout,
ScaffolderLayouts,
} from '@backstage/plugin-scaffolder-react';
import { TemplateListPage, TemplateWizardPage } from '../../next';
import { TemplateListPage, TemplateWizardPage } from '../../alpha/components';
jest.mock('../../next', () => ({
jest.mock('../../alpha/components', () => ({
TemplateWizardPage: jest.fn(() => null),
TemplateListPage: jest.fn(() => null),
}));
@@ -51,14 +51,14 @@ import {
TemplateListPageProps,
TemplateWizardPageProps,
} from '@backstage/plugin-scaffolder/alpha';
import { TemplateListPage, TemplateWizardPage } from '../../next';
import { TemplateListPage, TemplateWizardPage } from '../../alpha/components';
import { OngoingTask } from '../OngoingTask';
import {
TemplatePage,
TemplateFormPage,
TemplateEditorPage,
CustomFieldsPage,
} from '../../next/TemplateEditorPage';
} from '../../alpha/components/TemplateEditorPage';
/**
* The Props for the Scaffolder Router
@@ -28,7 +28,7 @@ import { GerritRepoPicker } from './GerritRepoPicker';
import { RepoUrlPickerHost } from './RepoUrlPickerHost';
import { RepoUrlPickerRepoName } from './RepoUrlPickerRepoName';
import { parseRepoPickerUrl, serializeRepoPickerUrl } from './utils';
import { RepoUrlPickerProps } from './schema';
import { RepoUrlPickerFieldSchema } from './schema';
import { RepoUrlPickerState } from './types';
import useDebounce from 'react-use/esm/useDebounce';
import { useTemplateSecrets } from '@backstage/plugin-scaffolder-react';
@@ -44,7 +44,9 @@ export { RepoUrlPickerSchema } from './schema';
*
* @public
*/
export const RepoUrlPicker = (props: RepoUrlPickerProps) => {
export const RepoUrlPicker = (
props: typeof RepoUrlPickerFieldSchema.TProps,
) => {
const { uiSchema, onChange, rawErrors, formData, schema } = props;
const [state, setState] = useState<RepoUrlPickerState>(
parseRepoPickerUrl(formData),
@@ -13,84 +13,86 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { z } from 'zod';
import { makeFieldSchemaFromZod } from '../utils';
import { makeFieldSchema } from '@backstage/plugin-scaffolder-react';
/**
* @public
*/
export const RepoUrlPickerFieldSchema = makeFieldSchemaFromZod(
z.string(),
z.object({
allowedHosts: z
.array(z.string())
.optional()
.describe('List of allowed SCM platform hosts'),
allowedOrganizations: z
.array(z.string())
.optional()
.describe('List of allowed organizations in the given SCM platform'),
allowedOwners: z
.array(z.string())
.optional()
.describe('List of allowed owners in the given SCM platform'),
allowedProjects: z
.array(z.string())
.optional()
.describe('List of allowed projects in the given SCM platform'),
allowedRepos: z
.array(z.string())
.optional()
.describe('List of allowed repos in the given SCM platform'),
requestUserCredentials: z
.object({
secretsKey: z
.string()
.describe(
'Key used within the template secrets context to store the credential',
),
additionalScopes: z
.object({
gitea: z
.array(z.string())
.optional()
.describe('Additional Gitea scopes to request'),
gerrit: z
.array(z.string())
.optional()
.describe('Additional Gerrit scopes to request'),
github: z
.array(z.string())
.optional()
.describe('Additional GitHub scopes to request'),
gitlab: z
.array(z.string())
.optional()
.describe('Additional GitLab scopes to request'),
bitbucket: z
.array(z.string())
.optional()
.describe('Additional BitBucket scopes to request'),
azure: z
.array(z.string())
.optional()
.describe('Additional Azure scopes to request'),
})
.optional()
.describe('Additional permission scopes to request'),
})
.optional()
.describe(
'If defined will request user credentials to auth against the given SCM platform',
),
}),
);
export const RepoUrlPickerFieldSchema = makeFieldSchema({
output: z => z.string(),
uiOptions: z =>
z.object({
allowedHosts: z
.array(z.string())
.optional()
.describe('List of allowed SCM platform hosts'),
allowedOrganizations: z
.array(z.string())
.optional()
.describe('List of allowed organizations in the given SCM platform'),
allowedOwners: z
.array(z.string())
.optional()
.describe('List of allowed owners in the given SCM platform'),
allowedProjects: z
.array(z.string())
.optional()
.describe('List of allowed projects in the given SCM platform'),
allowedRepos: z
.array(z.string())
.optional()
.describe('List of allowed repos in the given SCM platform'),
requestUserCredentials: z
.object({
secretsKey: z
.string()
.describe(
'Key used within the template secrets context to store the credential',
),
additionalScopes: z
.object({
gitea: z
.array(z.string())
.optional()
.describe('Additional Gitea scopes to request'),
gerrit: z
.array(z.string())
.optional()
.describe('Additional Gerrit scopes to request'),
github: z
.array(z.string())
.optional()
.describe('Additional GitHub scopes to request'),
gitlab: z
.array(z.string())
.optional()
.describe('Additional GitLab scopes to request'),
bitbucket: z
.array(z.string())
.optional()
.describe('Additional BitBucket scopes to request'),
azure: z
.array(z.string())
.optional()
.describe('Additional Azure scopes to request'),
})
.optional()
.describe('Additional permission scopes to request'),
})
.optional()
.describe(
'If defined will request user credentials to auth against the given SCM platform',
),
}),
});
/**
* The input props that can be specified under `ui:options` for the
* `RepoUrlPicker` field extension.
*
* @public
* @deprecated this will be removed as it's no longer used
*/
export type RepoUrlPickerUiOptions =
typeof RepoUrlPickerFieldSchema.uiOptionsType;
@@ -16,27 +16,21 @@
import { JSONSchema7 } from 'json-schema';
import { z } from 'zod';
import zodToJsonSchema from 'zod-to-json-schema';
import {
CustomFieldExtensionSchema,
FieldExtensionComponentProps,
} from '@backstage/plugin-scaffolder-react';
import { FieldSchema as FieldSchemaType } from '@backstage/plugin-scaffolder-react';
/**
* @public
* FieldSchema encapsulates a JSONSchema7 along with the
* matching FieldExtensionComponentProps type for a field extension.
* @deprecated - import from {@link @backstage/plugin-scaffolder-react#FieldSchema} instead
*/
export interface FieldSchema<TReturn, TUiOptions> {
readonly schema: CustomFieldExtensionSchema;
readonly type: FieldExtensionComponentProps<TReturn, TUiOptions>;
readonly uiOptionsType: TUiOptions;
}
export interface FieldSchema<T, P> extends FieldSchemaType<T, P> {}
/**
* @public
* @deprecated use `makeFieldSchema` instead
* Utility function to convert zod return and UI options schemas to a
* CustomFieldExtensionSchema with FieldExtensionComponentProps type inference
*/
export function makeFieldSchemaFromZod<
TReturnSchema extends z.ZodType,
TUiOptionsSchema extends z.ZodType = z.ZodType<any, any, {}>,
@@ -58,5 +52,6 @@ export function makeFieldSchemaFromZod<
},
type: null as any,
uiOptionsType: null as any,
TProps: undefined as any,
};
}
@@ -16,9 +16,9 @@
import { Overrides } from '@material-ui/core/styles/overrides';
import { StyleRules } from '@material-ui/core/styles/withStyles';
import { ScaffolderTemplateEditorClassKey } from './next/TemplateEditorPage/TemplateEditor';
import { ScaffolderTemplateFormPreviewerClassKey } from './next/TemplateEditorPage/TemplateFormPreviewer';
import { ScaffolderCustomFieldExplorerClassKey } from './next/TemplateEditorPage/CustomFieldExplorer';
import { ScaffolderTemplateEditorClassKey } from './alpha/components/TemplateEditorPage/TemplateEditor';
import { ScaffolderTemplateFormPreviewerClassKey } from './alpha/components/TemplateEditorPage/TemplateFormPreviewer';
import { ScaffolderCustomFieldExplorerClassKey } from './alpha/components/TemplateEditorPage/CustomFieldExplorer';
/** @public */
export type ScaffolderReactComponentsNameToClassKey = {
+11
View File
@@ -7200,6 +7200,7 @@ __metadata:
"@backstage/core-app-api": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/plugin-catalog": "workspace:^"
"@backstage/plugin-catalog-common": "workspace:^"
"@backstage/plugin-catalog-react": "workspace:^"
@@ -9947,6 +9948,16 @@ __metadata:
languageName: unknown
linkType: soft
"@internal/scaffolder@workspace:packages/scaffolder-internal":
version: 0.0.0-use.local
resolution: "@internal/scaffolder@workspace:packages/scaffolder-internal"
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/plugin-scaffolder-react": "workspace:^"
zod: ^3.22.4
languageName: unknown
linkType: soft
"@ioredis/commands@npm:^1.1.1":
version: 1.1.1
resolution: "@ioredis/commands@npm:1.1.1"