Merge pull request #2481 from RoadieHQ/scaffolder-values-validation

Scaffolder values validation
This commit is contained in:
Ben Lambert
2020-09-16 15:33:36 +02:00
committed by GitHub
4 changed files with 109 additions and 0 deletions
+1
View File
@@ -36,6 +36,7 @@
"git-url-parse": "^11.1.2",
"globby": "^11.0.0",
"helmet": "^4.0.0",
"jsonschema": "^1.2.6",
"morgan": "^1.10.0",
"nodegit": "0.26.5",
"uuid": "^8.2.0",
@@ -0,0 +1,93 @@
/*
* Copyright 2020 Spotify AB
*
* 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 { getVoidLogger } from '@backstage/backend-common';
import express from 'express';
import request from 'supertest';
import { createRouter } from './router';
import { Templaters, Preparers, PublisherBase } from '../scaffolder';
import Docker from 'dockerode';
jest.mock('dockerode');
describe('createRouter', () => {
let app: express.Express;
const publisher: jest.Mocked<PublisherBase> = { publish: jest.fn() };
beforeAll(async () => {
const router = await createRouter({
logger: getVoidLogger(),
preparers: new Preparers(),
templaters: new Templaters(),
publisher: publisher,
dockerClient: new Docker(),
});
app = express().use(router);
});
beforeEach(() => {
jest.resetAllMocks();
});
describe('POST /v1/jobs', () => {
const template = {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Template',
metadata: {
description: 'Create a new CRA website project',
name: 'create-react-app-template',
tags: ['experimental', 'react', 'cra'],
title: 'Create React App Template',
},
spec: {
owner: 'web@example.com',
path: '.',
schema: {
properties: {
component_id: {
description: 'Unique name of the component',
title: 'Name',
type: 'string',
},
description: {
description: 'Description of the component',
title: 'Description',
type: 'string',
},
use_typescript: {
default: true,
description: 'Include typescript',
title: 'Use Typescript',
type: 'boolean',
},
},
required: ['component_id', 'use_typescript'],
},
templater: 'cra',
type: 'website',
},
};
it('rejects template values which do not match the template schema definition', async () => {
const response = await request(app).post('/v1/jobs').send({
template,
values: {},
});
expect(response.status).toEqual(400);
});
});
});
@@ -28,6 +28,7 @@ import {
TemplaterBuilder,
PublisherBase,
} from '../scaffolder';
import { validate, ValidatorResult } from 'jsonschema';
export interface RouterOptions {
preparers: PreparerBuilder;
@@ -84,6 +85,15 @@ export async function createRouter(
const values: RequiredTemplateValues & Record<string, JsonValue> =
req.body.values;
const validationResult: ValidatorResult = validate(
values,
template.spec.schema,
);
if (!validationResult.valid) {
res.status(400).json({ errors: validationResult.errors });
return;
}
const job = jobProcessor.create({
entity: template,
values,
+5
View File
@@ -13288,6 +13288,11 @@ jsonpointer@^4.0.1:
resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk=
jsonschema@^1.2.6:
version "1.2.6"
resolved "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.6.tgz#52b0a8e9dc06bbae7295249d03e4b9faee8a0c0b"
integrity sha512-SqhURKZG07JyKKeo/ir24QnS4/BV7a6gQy93bUSe4lUdNp0QNpIz2c9elWJQ9dpc5cQYY6cvCzgRwy0MQCLyqA==
jspdf-autotable@3.5.3:
version "3.5.3"
resolved "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-3.5.3.tgz#2f73adb07f340e7dbf22950e3e6c8bf853991479"