Files
showbridge-webui/src/app/utils/params.ts
T
2026-04-04 08:51:20 -05:00

206 lines
6.9 KiB
TypeScript

import { FormGroup, ValidatorFn, Validators, FormControl, AbstractControl, ValidationErrors } from "@angular/forms";
import { SomeJSONSchema } from "ajv/dist/types/json-schema";
import { ParamsFormInfo } from "../models/form";
export function schemaToParamsFormInfo(schema: SomeJSONSchema): ParamsFormInfo {
const paramsFormInfo: ParamsFormInfo = {
formGroup: new FormGroup({}),
paramsInfo: {},
};
if (schema?.properties) {
const paramKeys = Object.keys(schema.properties);
Object.entries(schema.properties).forEach(([paramKey, paramSchema]: [string, any]) => {
if (paramSchema.type) {
switch (paramSchema.type) {
case 'string':
case 'number':
case 'integer':
case 'boolean':
case 'array': // TODO(jwetzell): actually handle arrays
case 'object': // TODO(jwetzell): actually handle objects
let formDefault = '';
const validators: ValidatorFn[] = [];
// NOTE(jwetzell): check for a default value to set
if (paramSchema.const) {
formDefault = paramSchema.const;
} else if (paramSchema.default) {
formDefault = paramSchema.default;
}
// NOTE(jwetzell): add as many validators as we can
if (paramSchema.minimum) {
validators.push(Validators.min(paramSchema.minimum));
}
if (paramSchema.maximum) {
validators.push(Validators.max(paramSchema.maximum));
}
if (paramSchema.pattern) {
validators.push(Validators.pattern(new RegExp(paramSchema.pattern)));
}
if (schema.required) {
if (schema.required.includes(paramKey)) {
validators.push(Validators.required);
}
}
paramsFormInfo.paramsInfo[paramKey] = {
key: paramKey,
display: paramSchema.title ? paramSchema.title : paramKey,
type: paramSchema.type,
hint: paramSchema.description ? paramSchema.description : undefined,
isConst: !!paramSchema.const,
schema: paramSchema,
placeholder: '',
default: paramSchema.default ? paramSchema.default : undefined,
};
if (paramSchema.examples && paramSchema.examples.length > 0) {
paramsFormInfo.paramsInfo[paramKey].placeholder = paramSchema.examples[0];
}
if (paramSchema.type === 'object') {
validators.push(objectValidator);
}
//TODO(jwetzell): figure out how to disable a control but not have to deal with undefined values on disabled controls
paramsFormInfo.formGroup.addControl(
paramKey,
new FormControl(formDefault, validators),
);
if (paramSchema.enum) {
paramsFormInfo.paramsInfo[paramKey].options = paramSchema.enum;
}
break;
default:
console.error(
`schema-service: unhandled param schema type for form group = ${paramSchema.type}`,
);
break;
}
} else {
console.error('schema-service: param property without type');
}
});
} else {
console.error('trigger-form: params schema without properties');
console.error(schema);
}
return paramsFormInfo;
}
export function cleanParams(paramsSchema: SomeJSONSchema, params: any): any {
Object.keys(params).forEach((paramKey) => {
if (paramsSchema.properties[paramKey]) {
const paramSchema = paramsSchema.properties[paramKey];
// delete null/undefined/empty params that aren't required
if (
params[paramKey] === undefined ||
params[paramKey] === null ||
params[paramKey] === ''
) {
if (paramSchema.required) {
if (!paramSchema.includes(paramKey)) {
delete params[paramKey];
return;
}
} else {
delete params[paramKey];
return;
}
}
if (paramSchema.type) {
switch (paramSchema.type) {
case 'integer':
var paramValue = parseInt(params[paramKey]);
// NOTE(jwetzell): delete non-numbers
if (Number.isNaN(paramValue)) {
delete params[paramKey];
} else {
params[paramKey] = paramValue;
}
break;
case 'number':
var paramValue = parseFloat(params[paramKey]);
// NOTE(jwetzell): delete non-numbers
if (Number.isNaN(paramValue)) {
delete params[paramKey];
} else {
params[paramKey] = paramValue;
}
break;
case 'array':
if (!Array.isArray(params[paramKey])) {
const paramValue = params[paramKey];
params[paramKey] = parseStringToArray(paramValue, paramSchema);
}
break;
case 'object':
try {
params[paramKey] = JSON.parse(params[paramKey]);
} catch (error) {
console.error('object param is not JSON');
}
break;
case 'string':
// string is default so nothing needs to happen to clean it's value
break;
default:
console.log(`schema-service: unhandled param schema type: ${paramSchema.type}`);
break;
}
}
}
});
return params;
}
function objectValidator(control: AbstractControl): ValidationErrors | null {
try {
JSON.parse(control.value);
return null;
} catch (error) {
return { json: true };
}
}
export function parseStringToArray(value: any, schema: SomeJSONSchema): any[] | undefined {
if (!Array.isArray(value)) {
const paramValue = value;
if (paramValue === undefined || paramValue.trim().length === 0) {
value = [];
} else {
if (schema.items?.type) {
if (schema.items?.type === 'integer') {
return paramValue
.split(',')
.map((part: string) => part.trim())
.map((item: any) => parseInt(item));
} else if (schema.items?.type === 'number') {
return paramValue
.split(',')
.map((part: string) => part.trim())
.map((item: any) => parseFloat(item));
} else if (schema.items?.type === 'string') {
return paramValue.split(',').map((part: string) => part.trim());
} else if (schema.items?.type === 'object') {
// TODO(jwetzell): this seems gross, not sure if this covers everything
return JSON.parse(`[${paramValue}]`);
} else {
console.error(`schema-service: unhandled array schema type: ${schema.items?.type}`);
}
} else {
// NOTE(jwetzell): default to comma-separated strings
return paramValue.split(',').map((part: string) => part.trim());
}
}
}
return undefined;
}