Expose template metadata to custom action handler
Currently, we only expose the template name to satisfy the requirement from the issue, but more can be added easily (as long as the type is expanded accordingly). Signed-off-by: Joon Park <joonp@spotify.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-common': patch
|
||||
---
|
||||
|
||||
Expose template metadata to custom action handler in Scaffolder.
|
||||
@@ -91,6 +91,8 @@ argument. It looks like the following:
|
||||
- `createTemporaryDirectory` a function to call to give you a temporary
|
||||
directory somewhere on the runner so you can store some files there rather
|
||||
than polluting the `workspacePath`
|
||||
- `ctx.metadata` - an object containing a `name` field, indicating the template
|
||||
name. More metadata fields may be added later.
|
||||
|
||||
### Registering Custom Actions
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ export type ActionContext<Input extends InputBase> = {
|
||||
input: Input;
|
||||
output(name: string, value: JsonValue): void;
|
||||
createTemporaryDirectory(): Promise<string>;
|
||||
metadata?: TemplateMetadata;
|
||||
};
|
||||
|
||||
// Warning: (ae-missing-release-tag) "CatalogEntityClient" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
@@ -432,6 +433,8 @@ export interface TaskSpecV1beta2 {
|
||||
// (undocumented)
|
||||
baseUrl?: string;
|
||||
// (undocumented)
|
||||
metadata?: TemplateMetadata;
|
||||
// (undocumented)
|
||||
output: {
|
||||
[name: string]: string;
|
||||
};
|
||||
@@ -454,6 +457,8 @@ export interface TaskSpecV1beta3 {
|
||||
// (undocumented)
|
||||
baseUrl?: string;
|
||||
// (undocumented)
|
||||
metadata?: TemplateMetadata;
|
||||
// (undocumented)
|
||||
output: {
|
||||
[name: string]: JsonValue;
|
||||
};
|
||||
@@ -558,4 +563,9 @@ export class TemplateActionRegistry {
|
||||
action: TemplateAction<Parameters>,
|
||||
): void;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type TemplateMetadata = {
|
||||
name: string;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Logger } from 'winston';
|
||||
import { Writable } from 'stream';
|
||||
import { JsonValue, JsonObject } from '@backstage/types';
|
||||
import { Schema } from 'jsonschema';
|
||||
import { TemplateMetadata } from '../tasks/types';
|
||||
|
||||
type PartialJsonObject = Partial<JsonObject>;
|
||||
type PartialJsonValue = PartialJsonObject | JsonValue | undefined;
|
||||
@@ -44,6 +45,8 @@ export type ActionContext<Input extends InputBase> = {
|
||||
* Creates a temporary directory for use by the action, which is then cleaned up automatically.
|
||||
*/
|
||||
createTemporaryDirectory(): Promise<string>;
|
||||
|
||||
metadata?: TemplateMetadata;
|
||||
};
|
||||
|
||||
export type TemplateAction<Input extends InputBase> = {
|
||||
|
||||
@@ -146,6 +146,30 @@ describe('DefaultWorkflowRunner', () => {
|
||||
|
||||
expect(fakeActionHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should pass metadata through', async () => {
|
||||
const templateName = 'template name';
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'scaffolder.backstage.io/v1beta3',
|
||||
parameters: {},
|
||||
output: {},
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'jest-validated-action',
|
||||
input: { foo: 1 },
|
||||
},
|
||||
],
|
||||
metadata: { name: templateName },
|
||||
});
|
||||
|
||||
await runner.execute(task);
|
||||
|
||||
expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({
|
||||
name: templateName,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('conditionals', () => {
|
||||
|
||||
@@ -225,6 +225,12 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner {
|
||||
input: JSON.stringify(input, null, 2),
|
||||
});
|
||||
|
||||
if (!task.spec.metadata) {
|
||||
console.warn(
|
||||
'DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.',
|
||||
);
|
||||
}
|
||||
|
||||
await action.handler({
|
||||
baseUrl: task.spec.baseUrl,
|
||||
logger: taskLogger,
|
||||
@@ -242,6 +248,7 @@ export class HandlebarsWorkflowRunner implements WorkflowRunner {
|
||||
output(name: string, value: JsonValue) {
|
||||
stepOutputs[name] = value;
|
||||
},
|
||||
metadata: task.spec.metadata,
|
||||
});
|
||||
|
||||
// Remove all temporary directories that were created when executing the action
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('LegacyWorkflowRunner', () => {
|
||||
let runner: HandlebarsWorkflowRunner;
|
||||
const logger = getVoidLogger();
|
||||
let actionRegistry = new TemplateActionRegistry();
|
||||
let fakeActionHandler: jest.Mock;
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(
|
||||
new ConfigReader({
|
||||
@@ -59,6 +60,11 @@ describe('LegacyWorkflowRunner', () => {
|
||||
ctx.output('badOutput', false);
|
||||
},
|
||||
});
|
||||
fakeActionHandler = jest.fn();
|
||||
actionRegistry.register({
|
||||
id: 'test-metadata-action',
|
||||
handler: fakeActionHandler,
|
||||
});
|
||||
|
||||
runner = new HandlebarsWorkflowRunner({
|
||||
actionRegistry,
|
||||
@@ -87,6 +93,30 @@ describe('LegacyWorkflowRunner', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should pass metadata through', async () => {
|
||||
const templateName = 'template name';
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'backstage.io/v1beta2',
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'test-metadata-action',
|
||||
input: { foo: 1 },
|
||||
},
|
||||
],
|
||||
output: {},
|
||||
values: {},
|
||||
metadata: { name: templateName },
|
||||
});
|
||||
|
||||
await runner.execute(task);
|
||||
|
||||
expect(fakeActionHandler.mock.calls[0][0].metadata).toEqual({
|
||||
name: templateName,
|
||||
});
|
||||
});
|
||||
|
||||
describe('templating', () => {
|
||||
it('should template the output', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
|
||||
@@ -228,6 +228,12 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
const tmpDirs = new Array<string>();
|
||||
const stepOutput: { [outputName: string]: JsonValue } = {};
|
||||
|
||||
if (!task.spec.metadata) {
|
||||
console.warn(
|
||||
'DEPRECATION NOTICE: metadata is undefined. metadata will be required in the future.',
|
||||
);
|
||||
}
|
||||
|
||||
await action.handler({
|
||||
baseUrl: task.spec.baseUrl,
|
||||
input,
|
||||
@@ -244,6 +250,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner {
|
||||
output(name: string, value: JsonValue) {
|
||||
stepOutput[name] = value;
|
||||
},
|
||||
metadata: task.spec.metadata,
|
||||
});
|
||||
|
||||
// Remove all temporary directories that were created when executing the action
|
||||
|
||||
@@ -34,4 +34,5 @@ export type {
|
||||
TaskContext,
|
||||
TaskStore,
|
||||
DispatchResult,
|
||||
TemplateMetadata,
|
||||
} from './types';
|
||||
|
||||
@@ -69,6 +69,15 @@ export type SerializedTaskEvent = {
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* TemplateMetadata
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TemplateMetadata = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* TaskSpecV1beta2
|
||||
*
|
||||
@@ -86,6 +95,7 @@ export interface TaskSpecV1beta2 {
|
||||
if?: string | boolean;
|
||||
}>;
|
||||
output: { [name: string]: string };
|
||||
metadata?: TemplateMetadata;
|
||||
}
|
||||
|
||||
export interface TaskStep {
|
||||
@@ -107,6 +117,7 @@ export interface TaskSpecV1beta3 {
|
||||
parameters: JsonObject;
|
||||
steps: TaskStep[];
|
||||
output: { [name: string]: JsonValue };
|
||||
metadata?: TemplateMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -209,6 +209,7 @@ export async function createRouter(
|
||||
name: step.name ?? step.action,
|
||||
})),
|
||||
output: template.spec.output ?? {},
|
||||
metadata: { name: template.metadata?.name },
|
||||
}
|
||||
: {
|
||||
apiVersion: template.apiVersion,
|
||||
@@ -220,6 +221,7 @@ export async function createRouter(
|
||||
name: step.name ?? step.action,
|
||||
})),
|
||||
output: template.spec.output ?? {},
|
||||
metadata: { name: template.metadata?.name },
|
||||
};
|
||||
} else {
|
||||
throw new InputError(
|
||||
|
||||
Reference in New Issue
Block a user