chore: added validation for schemas for actions
Signed-off-by: blam <ben@blam.sh> Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -55,6 +55,23 @@ describe('DefaultWorkflowRunner', () => {
|
||||
handler: fakeActionHandler,
|
||||
});
|
||||
|
||||
actionRegistry.register({
|
||||
id: 'jest-validated-action',
|
||||
description: 'Mock action for testing',
|
||||
handler: fakeActionHandler,
|
||||
schema: {
|
||||
input: {
|
||||
type: 'object',
|
||||
required: ['foo'],
|
||||
properties: {
|
||||
foo: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
actionRegistry.register({
|
||||
id: 'output-action',
|
||||
description: 'Mock action for testing',
|
||||
@@ -85,7 +102,40 @@ describe('DefaultWorkflowRunner', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('validation', () => {});
|
||||
describe('validation', () => {
|
||||
it('should throw an error if the action has a schema and the input does not match', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'backstage.io/v1beta3',
|
||||
parameters: {},
|
||||
output: {},
|
||||
steps: [{ id: 'test', name: 'name', action: 'jest-validated-action' }],
|
||||
});
|
||||
|
||||
await expect(runner.execute(task)).rejects.toThrowError(
|
||||
/Invalid input passed to action jest-validated-action, instance requires property \"foo\"/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should run the action when the validation passes', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
apiVersion: 'backstage.io/v1beta3',
|
||||
parameters: {},
|
||||
output: {},
|
||||
steps: [
|
||||
{
|
||||
id: 'test',
|
||||
name: 'name',
|
||||
action: 'jest-validated-action',
|
||||
input: { foo: 1 },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await runner.execute(task);
|
||||
|
||||
expect(fakeActionHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
describe('conditionals', () => {
|
||||
it('should execute steps conditionally', async () => {
|
||||
const task = createMockTaskWithSpec({
|
||||
|
||||
@@ -31,6 +31,7 @@ import { JsonObject, JsonValue } from '@backstage/config';
|
||||
import { InputError } from '@backstage/errors';
|
||||
import { PassThrough } from 'stream';
|
||||
import { isTruthy } from './helper';
|
||||
import { validate as validateJsonSchema } from 'jsonschema';
|
||||
|
||||
type Options = {
|
||||
workingDirectory: string;
|
||||
@@ -79,6 +80,8 @@ export class DefaultWorkflowRunner implements WorkflowRunner {
|
||||
private readonly nunjucks: nunjucks.Environment;
|
||||
|
||||
constructor(private readonly options: Options) {
|
||||
// TODO(blam): Probably need the repo helper here.
|
||||
// Or we move to returning Objects in the RepoUrlPickerV2 or something?
|
||||
this.nunjucks = nunjucks.configure({
|
||||
autoescape: false,
|
||||
tags: {
|
||||
@@ -164,7 +167,20 @@ export class DefaultWorkflowRunner implements WorkflowRunner {
|
||||
const action = this.options.actionRegistry.get(step.action);
|
||||
const { taskLogger, streamLogger } = createStepLogger({ task, step });
|
||||
|
||||
const input = step.input && this.render(step.input, context);
|
||||
const input = (step.input && this.render(step.input, context)) ?? {};
|
||||
|
||||
if (action.schema?.input) {
|
||||
const validateResult = validateJsonSchema(
|
||||
input,
|
||||
action.schema.input,
|
||||
);
|
||||
if (!validateResult.valid) {
|
||||
const errors = validateResult.errors.join(', ');
|
||||
throw new InputError(
|
||||
`Invalid input passed to action ${action.id}, ${errors}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const tmpDirs = new Array<string>();
|
||||
const stepOutput: { [outputName: string]: JsonValue } = {};
|
||||
|
||||
Reference in New Issue
Block a user