chore: added step extraction with tests

Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
blam
2022-03-24 11:55:49 +01:00
parent 06f44b95bd
commit f534f52696
2 changed files with 436 additions and 26 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2022 The Backstage Authors
* Copyright 2020 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.
@@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { extractSchemaFromManifest } from './schema';
describe('schema utils', () => {
it('should do stuff', () => {
const inputSchema = {
import { JsonObject } from '@backstage/types';
import { TemplateParameterSchema } from '../../../types';
import { extractSchemaFromStep } from './schema';
describe('extractSchemaFromStep', () => {
it('transforms deep schema', () => {
const inputSchema: JsonObject = {
type: 'object',
'ui:welp': 'warp',
properties: {
@@ -64,12 +67,347 @@ describe('schema utils', () => {
},
};
expect(
extractSchemaFromManifest({
title: 'test',
steps: [{ title: 'test', schema: inputSchema }],
}),
).toEqual({
expect(extractSchemaFromStep(inputSchema)).toEqual({
schema: expectedSchema,
uiSchema: expectedUiSchema,
});
});
it('transforms schema with anyOf fields', () => {
const inputSchema: JsonObject = {
type: 'object',
anyOf: [
{
properties: {
field3: {
type: 'string',
default: 'Value 1',
'ui:readonly': true,
},
},
},
{
properties: {
field3: {
type: 'string',
default: 'Value 2',
'ui:readonly': true,
},
},
},
],
oneOf: [
{
properties: {
field4: {
type: 'string',
default: 'Value 1',
'ui:readonly': true,
},
},
},
],
allOf: [
{
properties: {
field5: {
type: 'string',
default: 'Value 1',
'ui:readonly': true,
},
},
},
],
properties: {
field1: {
type: 'object',
anyOf: [
{
properties: {
field3: {
type: 'string',
default: 'Value 1',
'ui:readonly': true,
},
},
},
{
properties: {
field3: {
type: 'string',
default: 'Value 2',
'ui:readonly': true,
},
},
},
],
oneOf: [
{
properties: {
field4: {
type: 'string',
default: 'Value 1',
'ui:readonly': true,
},
},
},
],
allOf: [
{
properties: {
field5: {
type: 'string',
default: 'Value 1',
'ui:readonly': true,
},
},
},
],
},
field2: {
type: 'string',
'ui:derp': 'xerp',
},
},
};
const expectedSchema = {
type: 'object',
anyOf: [
{
properties: {
field3: {
type: 'string',
default: 'Value 1',
},
},
},
{
properties: {
field3: {
type: 'string',
default: 'Value 2',
},
},
},
],
oneOf: [
{
properties: {
field4: {
type: 'string',
default: 'Value 1',
},
},
},
],
allOf: [
{
properties: {
field5: {
type: 'string',
default: 'Value 1',
},
},
},
],
properties: {
field1: {
type: 'object',
anyOf: [
{
properties: {
field3: {
type: 'string',
default: 'Value 1',
},
},
},
{
properties: {
field3: {
type: 'string',
default: 'Value 2',
},
},
},
],
oneOf: [
{
properties: {
field4: {
type: 'string',
default: 'Value 1',
},
},
},
],
allOf: [
{
properties: {
field5: {
type: 'string',
default: 'Value 1',
},
},
},
],
},
field2: {
type: 'string',
},
},
};
const expectedUiSchema = {
field3: {
'ui:readonly': true,
},
field4: {
'ui:readonly': true,
},
field5: {
'ui:readonly': true,
},
field1: {
field3: {
'ui:readonly': true,
},
field4: {
'ui:readonly': true,
},
field5: {
'ui:readonly': true,
},
},
field2: {
'ui:derp': 'xerp',
},
};
expect(extractSchemaFromStep(inputSchema)).toEqual({
schema: expectedSchema,
uiSchema: expectedUiSchema,
});
});
it('transforms schema with dependencies', () => {
const inputSchema: JsonObject = {
type: 'object',
properties: {
name: {
type: 'string',
},
credit_card: {
type: 'number',
},
},
required: ['name'],
dependencies: {
credit_card: {
properties: {
billing_address: {
type: 'string',
'ui:widget': 'textarea',
},
},
required: ['billing_address'],
},
},
};
const expectedSchema = {
type: 'object',
properties: {
name: {
type: 'string',
},
credit_card: {
type: 'number',
},
},
required: ['name'],
dependencies: {
credit_card: {
properties: {
billing_address: {
type: 'string',
},
},
required: ['billing_address'],
},
},
};
const expectedUiSchema = {
billing_address: {
'ui:widget': 'textarea',
},
credit_card: {},
name: {},
};
expect(extractSchemaFromStep(inputSchema)).toEqual({
schema: expectedSchema,
uiSchema: expectedUiSchema,
});
});
it('transforms schema with array items', () => {
const inputSchema: JsonObject = {
type: 'object',
properties: {
person: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
},
address: {
type: 'string',
'ui:widget': 'textarea',
},
},
},
},
accountNumber: {
type: 'number',
},
},
};
const expectedSchema = {
type: 'object',
properties: {
person: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
},
address: {
type: 'string',
},
},
},
},
accountNumber: {
type: 'number',
},
},
};
const expectedUiSchema = {
accountNumber: {},
person: {
items: {
name: {},
address: {
'ui:widget': 'textarea',
},
},
},
};
expect(extractSchemaFromStep(inputSchema)).toEqual({
schema: expectedSchema,
uiSchema: expectedUiSchema,
});
@@ -13,27 +13,99 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { JsonObject } from '@backstage/types';
import { UiSchema } from '@rjsf/core';
import { JSONSchema7 } from 'json-schema';
import { TemplateParameterSchema } from '../../../types';
export const extractSchemaFromManifest = (
manifest: TemplateParameterSchema,
): { uiSchema: UiSchema; schema: JSONSchema7 } => {
const schema = manifest.steps[0].schema;
const uiSchema = JSON.parse(JSON.stringify(schema), (key, value) => {
if (typeof value === 'object') {
return Object.fromEntries(
Object.entries(value).filter(
([k, v]) => k.startsWith('ui:') || typeof v === 'object',
),
);
function isObject(value: unknown): value is JsonObject {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function extractUiSchema(schema: JsonObject, uiSchema: JsonObject) {
if (!isObject(schema)) {
return;
}
const { properties, items, anyOf, oneOf, allOf, dependencies } = schema;
for (const propName in schema) {
if (!schema.hasOwnProperty(propName)) {
continue;
}
return value;
});
if (propName.startsWith('ui:')) {
uiSchema[propName] = schema[propName];
delete schema[propName];
}
}
console.log(uiSchema);
return { uiSchema, schema: {} };
if (isObject(properties)) {
for (const propName in properties) {
if (!properties.hasOwnProperty(propName)) {
continue;
}
const schemaNode = properties[propName];
if (!isObject(schemaNode)) {
continue;
}
const innerUiSchema = {};
uiSchema[propName] = innerUiSchema;
extractUiSchema(schemaNode, innerUiSchema);
}
}
if (isObject(items)) {
const innerUiSchema = {};
uiSchema.items = innerUiSchema;
extractUiSchema(items, innerUiSchema);
}
if (Array.isArray(anyOf)) {
for (const schemaNode of anyOf) {
if (!isObject(schemaNode)) {
continue;
}
extractUiSchema(schemaNode, uiSchema);
}
}
if (Array.isArray(oneOf)) {
for (const schemaNode of oneOf) {
if (!isObject(schemaNode)) {
continue;
}
extractUiSchema(schemaNode, uiSchema);
}
}
if (Array.isArray(allOf)) {
for (const schemaNode of allOf) {
if (!isObject(schemaNode)) {
continue;
}
extractUiSchema(schemaNode, uiSchema);
}
}
if (isObject(dependencies)) {
for (const depName of Object.keys(dependencies)) {
const schemaNode = dependencies[depName];
if (!isObject(schemaNode)) {
continue;
}
extractUiSchema(schemaNode, uiSchema);
}
}
}
export const extractSchemaFromStep = (
inputStep: JSONSchema7,
): { uiSchema: UiSchema; schema: JSONSchema7 } => {
const uiSchema: UiSchema = {};
const returnSchema: JsonObject = JSON.parse(JSON.stringify(inputStep));
extractUiSchema(returnSchema, uiSchema);
return { uiSchema, schema: returnSchema };
};