151 lines
3.8 KiB
TypeScript
151 lines
3.8 KiB
TypeScript
/**
|
|
* @author Martin Karkowski
|
|
* @email m.karkowski@zema.de
|
|
* @create date 2020-11-06 08:53:55
|
|
* @modify date 2020-11-06 08:53:56
|
|
* @desc [description]
|
|
*/
|
|
|
|
import { IJsonSchema } from "../types/IJSONSchema";
|
|
import { INopeDescriptor } from "../types/nope";
|
|
import { flattenObject, rgetattr, rsetattr, SPLITCHAR } from "./objectMethods";
|
|
|
|
/**
|
|
* Function to Flatten a JSON-Schema.
|
|
* @param schema
|
|
*/
|
|
export function flattenSchema(schema: IJsonSchema) {
|
|
let counter = 10000;
|
|
let flattenSchema = flattenObject(schema);
|
|
|
|
const getRefKeys = (flattenSchema: Map<string, any>) => {
|
|
const relevantKeys: Array<{ schemaPath: string; searchPath: string }> = [];
|
|
|
|
for (const [key, value] of flattenSchema) {
|
|
if (key.endsWith("$ref")) {
|
|
relevantKeys.push({
|
|
schemaPath: key,
|
|
searchPath: value.replace("#/", "").replace("/", SPLITCHAR),
|
|
});
|
|
}
|
|
}
|
|
return relevantKeys;
|
|
};
|
|
|
|
let refs = getRefKeys(flattenSchema);
|
|
while (refs.length > 0) {
|
|
counter--;
|
|
|
|
if (counter === 0) {
|
|
throw Error("Max amount of Recursions performed");
|
|
}
|
|
|
|
for (const ref of refs) {
|
|
const subSchema = rgetattr(schema, ref.searchPath, null, ".");
|
|
rsetattr(schema, ref.schemaPath.replace(".$ref", ""), subSchema);
|
|
}
|
|
|
|
flattenSchema = flattenObject(schema);
|
|
refs = getRefKeys(flattenSchema);
|
|
}
|
|
|
|
return schema as IJsonSchema;
|
|
}
|
|
|
|
/**
|
|
* Function to get a Schemas Definition
|
|
* @param schema the JSON-Schema
|
|
* @param reference the path of the relevant definition.
|
|
*/
|
|
export function schemaGetDefinition(schema: IJsonSchema, reference: string) {
|
|
return rgetattr(schema, reference.replace("#/", ""), null, "/");
|
|
}
|
|
|
|
const _isNopeDescriptor: Array<
|
|
[keyof INopeDescriptor, (value: any) => boolean]
|
|
> = [
|
|
["type", (value) => value === "function"],
|
|
["inputs", (value) => typeof value === "object"],
|
|
["outputs", (value) => typeof value === "object"],
|
|
];
|
|
|
|
/**
|
|
* A Helper Function, to test, if the given schema is a JSON Schema or whether it contains a method description
|
|
*
|
|
* @param { INopeDescriptor | IJsonSchema } schema The Schema to Test
|
|
* @returns {boolean}
|
|
*/
|
|
export function isJsonSchema(schema: INopeDescriptor | IJsonSchema): boolean {
|
|
for (const [attr, test] of _isNopeDescriptor) {
|
|
if (test(schema[attr])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Object-Related Test-Cases.
|
|
if (
|
|
schema.type == "object" ||
|
|
(Array.isArray(schema.type) && schema.type.includes("object"))
|
|
) {
|
|
if (schema.properties) {
|
|
for (const key in schema.properties) {
|
|
if (!isJsonSchema(schema.properties[key])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (schema.patternProperties) {
|
|
for (const key in schema.patternProperties) {
|
|
if (!isJsonSchema(schema.patternProperties[key])) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (schema.dependencies) {
|
|
for (const key in schema.dependencies) {
|
|
if (
|
|
typeof schema.dependencies[key] !== "string" &&
|
|
!isJsonSchema(schema.dependencies[key] as INopeDescriptor)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
for (const key of ["allOf", "anyOf", "oneOf"]) {
|
|
if (schema[key]) {
|
|
for (const subSchema of schema[key]) {
|
|
if (!isJsonSchema(subSchema)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (
|
|
schema.type == "array" ||
|
|
(Array.isArray(schema.type) && schema.type.includes("array"))
|
|
) {
|
|
if (Array.isArray(schema.items)) {
|
|
for (const key in schema.items) {
|
|
if (!isJsonSchema(schema.items[key])) {
|
|
return false;
|
|
}
|
|
}
|
|
} else if (!isJsonSchema(schema.items)) {
|
|
return false;
|
|
}
|
|
|
|
// Test the Additional Items.
|
|
if (typeof schema.additionalItems === "object") {
|
|
if (!isJsonSchema(schema.additionalItems)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|