chore: added step extraction with tests
Signed-off-by: blam <ben@blam.sh>
This commit is contained in:
@@ -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 };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user