Files
budget-forecast/gui/app/src/providers/jsonschema-provider.tsx

192 lines
5.9 KiB
TypeScript

import { RJSFSchema } from '@rjsf/utils';
const API_URL = "http://localhost:8000";
export const jsonschemaProvider = {
getResourceSchema: async (resourceName: string): RJSFSchema => {
return buildResource(await getJsonschema(), resourceName)
}
};
let rawSchema: RJSFSchema;
const getJsonschema = async (): RJSFSchema => {
if (rawSchema === undefined) {
const response = await fetch(
API_URL + "/openapi.json",
)
rawSchema = await response.json();
}
return rawSchema;
}
function buildResource(rawSchemas: RJSFSchema, resourceName: string) {
let resource;
resource = structuredClone(rawSchemas.components.schemas[resourceName]);
resource.components = { schemas: {} };
for (let prop_name in resource.properties) {
let prop = resource.properties[prop_name];
if (is_reference(prop)) {
resolveReference(rawSchemas, resource, prop);
} else if (is_union(prop)) {
const union = prop.hasOwnProperty("oneOf") ? prop.oneOf : prop.anyOf;
for (let i in union) {
if (is_reference(union[i])) {
resolveReference(rawSchemas, resource, union[i]);
}
}
} else if (is_enum(prop)) {
for (let i in prop.allOf) {
if (is_reference(prop.allOf[i])) {
resolveReference(rawSchemas, resource, prop.allOf[i]);
}
}
} else if (is_array(prop) && is_reference(prop.items)) {
resolveReference(rawSchemas, resource, prop.items);
}
}
return resource;
}
function resolveReference(rawSchemas: RJSFSchema, resource: any, prop_reference: any) {
const subresourceName = get_reference_name(prop_reference);
const subresource = buildResource(rawSchemas, subresourceName);
resource.components.schemas[subresourceName] = subresource;
for (let subsubresourceName in subresource.components.schemas) {
if (! resource.components.schemas.hasOwnProperty(subsubresourceName)) {
resource.components.schemas[subsubresourceName] = subresource.components.schemas[subsubresourceName];
}
}
}
function changePropertiesOrder(resource: any) {
let created_at;
let updated_at;
let new_properties: any = {};
for (let prop_name in resource.properties) {
if (prop_name == 'created_at') {
created_at = resource.properties[prop_name];
} else if (prop_name == 'updated_at') {
updated_at = resource.properties[prop_name];
} else {
new_properties[prop_name] = resource.properties[prop_name];
}
}
if (created_at) {
new_properties['created_at'] = created_at;
}
if (updated_at) {
new_properties['updated_at'] = updated_at;
}
resource.properties = new_properties
}
function is_object(prop: any) {
return prop.hasOwnProperty('properties')
}
function is_reference(prop: any) {
return prop.hasOwnProperty('$ref');
}
function is_array(prop: any) {
return prop.hasOwnProperty('items');
}
function is_union(prop: any) {
return prop.hasOwnProperty('oneOf') || prop.hasOwnProperty('anyOf');
}
function is_enum(prop: any) {
return prop.hasOwnProperty('enum');
}
function get_reference_name(prop: any) {
return prop['$ref'].substring(prop['$ref'].lastIndexOf('/')+1);
}
function has_descendant(rawSchemas:RJSFSchema, resource: RJSFSchema, property_name: string): boolean {
if (is_array(resource)) {
return property_name == 'items';
} else if (is_object(resource)) {
return property_name in resource.properties!;
} else if (is_reference(resource)) {
let subresourceName = get_reference_name(resource);
return has_descendant(rawSchemas, buildResource(rawSchemas, subresourceName), property_name);
} else if (is_union(resource)) {
const union = resource.hasOwnProperty("oneOf") ? resource.oneOf : resource.anyOf;
for (const ref of union) {
return has_descendant(rawSchemas, ref, property_name)
}
} else if (is_enum(resource)) {
for (const ref of resource.allOf!) {
return has_descendant(rawSchemas, ref, property_name);
}
}
throw new Error("Jsonschema format not implemented in property finder");
}
function get_descendant(rawSchemas: RJSFSchema, resource: RJSFSchema, property_name: string): RJSFSchema {
if (is_array(resource) && property_name == 'items') {
return resource.items;
} else if (is_object(resource) && property_name in resource.properties!) {
return resource.properties[property_name];
} else if (is_reference(resource)) {
let subresourceName = get_reference_name(resource);
let subresource = buildResource(rawSchemas, subresourceName);
return get_descendant(rawSchemas, subresource, property_name);
} else if (is_union(resource)) {
for (const ref of resource.oneOf!) {
if (has_descendant(rawSchemas, ref, property_name)) {
return get_descendant(rawSchemas, ref, property_name);
}
}
} else if (is_enum(resource)) {
for (const ref of resource.allOf!) {
if (has_descendant(rawSchemas, ref, property_name)) {
return get_descendant(rawSchemas, ref, property_name);
}
}
}
throw new Error("property not found or Jsonschema format not implemented");
}
function path_exists(rawSchemas: RJSFSchema, resource: RJSFSchema, path: string): boolean{
const pointFirstPosition = path.indexOf('.')
if (pointFirstPosition == -1) {
return has_descendant(rawSchemas, resource, path);
}
return has_descendant(rawSchemas, resource, path.substring(0, pointFirstPosition))
&& path_exists(
rawSchemas,
get_descendant(
rawSchemas,
resource,
path.substring(0, pointFirstPosition)
),
path.substring(pointFirstPosition + 1)
);
}
function get_property_by_path(rawSchemas: RJSFSchema, resource: RJSFSchema, path: string): RJSFSchema {
const pointFirstPosition = path.indexOf('.')
if (pointFirstPosition == -1) {
return get_descendant(rawSchemas, resource, path);
}
return get_property_by_path(
rawSchemas,
get_descendant(
rawSchemas,
resource,
path.substring(0, pointFirstPosition)
),
path.substring(pointFirstPosition + 1)
);
}