Merge pull request #25975 from backstage/blam/secret-collection
scaffolder: Implement hooks for collecting secrets
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { AnyApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiHolder } from '@backstage/core-plugin-api';
|
||||
import { ComponentType } from 'react';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
@@ -75,6 +76,39 @@ export function createFormField<
|
||||
TUiOptions extends z.ZodType,
|
||||
>(opts: FormFieldExtensionData<TReturnValue, TUiOptions>): FormField;
|
||||
|
||||
// @alpha
|
||||
export function createScaffolderFormDecorator<
|
||||
TInputSchema extends {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
} = {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
TDeps extends {
|
||||
[key in string]: AnyApiRef;
|
||||
} = {
|
||||
[key in string]: AnyApiRef;
|
||||
},
|
||||
TInput extends JsonObject = {
|
||||
[key in keyof TInputSchema]: z.infer<ReturnType<TInputSchema[key]>>;
|
||||
},
|
||||
>(options: {
|
||||
id: string;
|
||||
schema?: {
|
||||
input?: TInputSchema;
|
||||
};
|
||||
deps?: TDeps;
|
||||
decorator: (
|
||||
ctx: ScaffolderFormDecoratorContext<TInput>,
|
||||
deps: TDeps extends {
|
||||
[key in string]: AnyApiRef;
|
||||
}
|
||||
? {
|
||||
[key in keyof TDeps]: TDeps[key]['T'];
|
||||
}
|
||||
: never,
|
||||
) => Promise<void>;
|
||||
}): ScaffolderFormDecorator<TInput>;
|
||||
|
||||
// @alpha
|
||||
export const DefaultTemplateOutputs: (props: {
|
||||
output?: ScaffolderTaskOutput;
|
||||
@@ -190,6 +224,27 @@ export interface ScaffolderFieldProps {
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type ScaffolderFormDecorator<TInput extends JsonObject = JsonObject> = {
|
||||
readonly $$type: '@backstage/scaffolder/FormDecorator';
|
||||
readonly id: string;
|
||||
readonly TInput: TInput;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type ScaffolderFormDecoratorContext<
|
||||
TInput extends JsonObject = JsonObject,
|
||||
> = {
|
||||
input: TInput;
|
||||
formState: Record<string, JsonValue>;
|
||||
setFormState: (
|
||||
fn: (currentState: Record<string, JsonValue>) => Record<string, JsonValue>,
|
||||
) => void;
|
||||
setSecrets: (
|
||||
fn: (currentState: Record<string, string>) => Record<string, string>,
|
||||
) => void;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function ScaffolderPageContextMenu(
|
||||
props: ScaffolderPageContextMenuProps,
|
||||
@@ -346,9 +401,9 @@ export const useFormDataFromQuery: (
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const useTemplateParameterSchema: (templateRef: string) => {
|
||||
manifest: TemplateParameterSchema | undefined;
|
||||
manifest?: TemplateParameterSchema | undefined;
|
||||
loading: boolean;
|
||||
error: Error | undefined;
|
||||
error?: Error | undefined;
|
||||
};
|
||||
|
||||
// @alpha
|
||||
|
||||
@@ -538,6 +538,10 @@ export type TemplateParameterSchema = {
|
||||
description?: string;
|
||||
schema: JsonObject;
|
||||
}>;
|
||||
EXPERIMENTAL_formDecorators?: {
|
||||
id: string;
|
||||
input?: JsonObject;
|
||||
}[];
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -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 * from './createScaffolderFieldExtension';
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 { createScaffolderFormDecorator } from './createScaffolderFormDecorator';
|
||||
|
||||
describe('createScaffolderFormDecorator', () => {
|
||||
it('should return a decorator', () => {
|
||||
const decorator = createScaffolderFormDecorator({
|
||||
id: 'test',
|
||||
deps: {},
|
||||
decorator: async () => {},
|
||||
});
|
||||
|
||||
expect(decorator).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/scaffolder/FormDecorator",
|
||||
"TInput": null,
|
||||
"decorator": [Function],
|
||||
"deps": {},
|
||||
"id": "test",
|
||||
"version": "v1",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should allow passing schema and be typesafe', () => {
|
||||
const decorator = createScaffolderFormDecorator({
|
||||
id: 'test',
|
||||
deps: {},
|
||||
schema: {
|
||||
input: {
|
||||
name: z => z.string(),
|
||||
age: z => z.number(),
|
||||
},
|
||||
},
|
||||
decorator: async ctx => {
|
||||
const name: string = ctx.input.name;
|
||||
|
||||
// @ts-expect-error
|
||||
const number: string = ctx.input.age;
|
||||
|
||||
expect([name, number]).toBeDefined();
|
||||
},
|
||||
});
|
||||
|
||||
expect(decorator).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/scaffolder/FormDecorator",
|
||||
"TInput": null,
|
||||
"decorator": [Function],
|
||||
"deps": {},
|
||||
"id": "test",
|
||||
"schema": {
|
||||
"input": {
|
||||
"age": [Function],
|
||||
"name": [Function],
|
||||
},
|
||||
},
|
||||
"version": "v1",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 { AnyApiRef } from '@backstage/core-plugin-api';
|
||||
import { JsonObject, JsonValue } from '@backstage/types';
|
||||
import { OpaqueFormDecorator } from '@internal/scaffolder';
|
||||
import { z } from 'zod';
|
||||
|
||||
/** @alpha */
|
||||
export type ScaffolderFormDecoratorContext<
|
||||
TInput extends JsonObject = JsonObject,
|
||||
> = {
|
||||
input: TInput;
|
||||
formState: Record<string, JsonValue>;
|
||||
|
||||
setFormState: (
|
||||
fn: (currentState: Record<string, JsonValue>) => Record<string, JsonValue>,
|
||||
) => void;
|
||||
setSecrets: (
|
||||
fn: (currentState: Record<string, string>) => Record<string, string>,
|
||||
) => void;
|
||||
};
|
||||
|
||||
/** @alpha */
|
||||
export type ScaffolderFormDecorator<TInput extends JsonObject = JsonObject> = {
|
||||
readonly $$type: '@backstage/scaffolder/FormDecorator';
|
||||
readonly id: string;
|
||||
readonly TInput: TInput;
|
||||
};
|
||||
|
||||
/**
|
||||
* Method for creating decorators which can be used to collect
|
||||
* secrets from the user before submitting to the backend.
|
||||
* @alpha
|
||||
*/
|
||||
export function createScaffolderFormDecorator<
|
||||
TInputSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType } = {
|
||||
[key in string]: (zImpl: typeof z) => z.ZodType;
|
||||
},
|
||||
TDeps extends { [key in string]: AnyApiRef } = { [key in string]: AnyApiRef },
|
||||
TInput extends JsonObject = {
|
||||
[key in keyof TInputSchema]: z.infer<ReturnType<TInputSchema[key]>>;
|
||||
},
|
||||
>(options: {
|
||||
id: string;
|
||||
schema?: {
|
||||
input?: TInputSchema;
|
||||
};
|
||||
deps?: TDeps;
|
||||
decorator: (
|
||||
ctx: ScaffolderFormDecoratorContext<TInput>,
|
||||
deps: TDeps extends { [key in string]: AnyApiRef }
|
||||
? { [key in keyof TDeps]: TDeps[key]['T'] }
|
||||
: never,
|
||||
) => Promise<void>;
|
||||
}): ScaffolderFormDecorator<TInput> {
|
||||
return OpaqueFormDecorator.createInstance('v1', {
|
||||
...options,
|
||||
TInput: null as unknown as TInput,
|
||||
} as {
|
||||
id: string;
|
||||
schema?: {
|
||||
input?: TInputSchema;
|
||||
};
|
||||
TInput: TInput;
|
||||
deps?: TDeps;
|
||||
decorator: (
|
||||
ctx: ScaffolderFormDecoratorContext,
|
||||
deps: { [key in string]: AnyApiRef['T'] },
|
||||
) => Promise<void>;
|
||||
});
|
||||
}
|
||||
@@ -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 * from './createScaffolderFormDecorator';
|
||||
@@ -22,15 +22,21 @@ import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export const useTemplateParameterSchema = (templateRef: string) => {
|
||||
export const useTemplateParameterSchema = (
|
||||
templateRef: string,
|
||||
): { manifest?: TemplateParameterSchema; loading: boolean; error?: Error } => {
|
||||
const scaffolderApi = useApi(scaffolderApiRef);
|
||||
const { value, loading, error } = useAsync(
|
||||
const {
|
||||
value: manifest,
|
||||
loading,
|
||||
error,
|
||||
} = useAsync(
|
||||
() => scaffolderApi.getTemplateParameterSchema(templateRef),
|
||||
[scaffolderApi, templateRef],
|
||||
);
|
||||
|
||||
return {
|
||||
manifest: value as TemplateParameterSchema | undefined,
|
||||
manifest,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
|
||||
@@ -18,3 +18,4 @@ export * from './lib';
|
||||
export * from './hooks';
|
||||
export * from './overridableComponents';
|
||||
export * from './blueprints';
|
||||
export * from './extensions';
|
||||
|
||||
@@ -33,4 +33,5 @@ export type TemplateParameterSchema = {
|
||||
description?: string;
|
||||
schema: JsonObject;
|
||||
}>;
|
||||
EXPERIMENTAL_formDecorators?: { id: string; input?: JsonObject }[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user