rework params-form logic out of schema service

This commit is contained in:
Joel Wetzell
2026-04-03 18:48:06 -05:00
parent 71b9eb6ec5
commit 653c65c00f
6 changed files with 368 additions and 337 deletions
@@ -5,6 +5,7 @@ import { MatIconModule } from '@angular/material/icon';
import { ParamInfo } from '../../models/form.model'; import { ParamInfo } from '../../models/form.model';
import { ListsService } from '../../services/lists.service'; import { ListsService } from '../../services/lists.service';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { parseStringToArray } from '../../utils/params.utils';
@Component({ @Component({
selector: 'app-array-form', selector: 'app-array-form',
@@ -30,7 +31,7 @@ export class ArrayFormComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
if (this.paramFormControl && this.paramInfo?.schema) { if (this.paramFormControl && this.paramInfo?.schema) {
if (!Array.isArray(this.paramFormControl.value)) { if (!Array.isArray(this.paramFormControl.value)) {
this.arrayValue = this.schemaService.parseStringToArray( this.arrayValue = parseStringToArray(
this.paramFormControl.value, this.paramFormControl.value,
this.paramInfo.schema, this.paramInfo.schema,
); );
@@ -1,15 +1,4 @@
@if (paramsSchema && paramsFormInfo) { @if (paramsSchema && paramsFormInfo) {
@if (paramsOptions.length > 1) {
<mat-tab-group
dynamicHeight
[selectedIndex]="paramsOptionsSelectedIndex"
(selectedTabChange)="paramsOptionsTabSelected($event)"
>
@for (paramsOption of paramsOptions; track paramsOption) {
<mat-tab [label]="paramsOption.display"></mat-tab>
}
</mat-tab-group>
}
<form [formGroup]="paramsFormInfo.formGroup"> <form [formGroup]="paramsFormInfo.formGroup">
<div class="flex flex-col"> <div class="flex flex-col">
@for (key of paramKeys(); track key) { @for (key of paramKeys(); track key) {
@@ -0,0 +1,95 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { ParamsFormComponent } from './params-form.component';
import { SchemaService } from '../../services/schema.service';
import { SomeJSONSchema } from 'ajv/dist/types/json-schema';
describe('ParamsForm', ()=>{
let component: ParamsFormComponent;
let fixture: ComponentFixture<ParamsFormComponent>;
beforeEach(async ()=>{
fixture = TestBed.createComponent(ParamsFormComponent);
component = fixture.componentInstance;
await fixture.whenStable();
})
it('should create', ()=>{
expect(component).toBeTruthy();
})
it('should create form for schema', async ()=>{
const paramsSchema: SomeJSONSchema = {
type: "object",
properties: {},
required: []
}
fixture.componentRef.setInput('paramsSchema', paramsSchema)
await fixture.whenStable()
expect(fixture.nativeElement.querySelector('form')).toBeTruthy()
})
it('form should have input for string property', async ()=>{
const paramsSchema: SomeJSONSchema = {
type: "object",
properties: {
"test": {
type: "string",
}
},
required: []
}
fixture.componentRef.setInput('paramsSchema', paramsSchema)
await fixture.whenStable()
const formEl = fixture.nativeElement.querySelector('form')
expect(formEl).toBeDefined()
const inputEl = formEl.querySelector('input')
expect(inputEl).toBeDefined()
})
it('form should have select for string enum property', async ()=>{
const paramsSchema: SomeJSONSchema = {
type: "object",
properties: {
"test": {
type: "string",
enum: ['one','two','three']
}
},
required: []
}
fixture.componentRef.setInput('paramsSchema', paramsSchema)
await fixture.whenStable()
const formEl = fixture.nativeElement.querySelector('form')
expect(formEl).toBeDefined()
const selectEl = formEl.querySelector('select')
expect(selectEl).toBeDefined()
})
it('form input should reflect data', async ()=>{
const paramsSchema: SomeJSONSchema = {
type: "object",
properties: {
"test": {
type: "string",
}
},
required: []
}
fixture.componentRef.setInput('paramsSchema', paramsSchema)
await fixture.whenStable()
fixture.componentRef.setInput('data', {
test: 'hello'
})
await fixture.whenStable()
const inputEl = fixture.nativeElement.querySelector('input')
expect(inputEl).toBeDefined()
expect(inputEl.value).toBe('hello')
fixture.componentRef.setInput('data', {
test: 'changed'
})
await fixture.whenStable()
expect(inputEl.value).toBe('changed')
})
})
@@ -1,18 +1,18 @@
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { Component, inject, Input, OnInit, output } from '@angular/core'; import { Component, inject, Input, OnChanges, OnDestroy, OnInit, output, SimpleChange, SimpleChanges } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms'; import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { MatTabChangeEvent, MatTabsModule } from '@angular/material/tabs'; import { MatTabsModule } from '@angular/material/tabs';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { SomeJSONSchema } from 'ajv/dist/types/json-schema'; import { SomeJSONSchema } from 'ajv/dist/types/json-schema';
import { cloneDeep, has } from 'lodash-es'; import { cloneDeep, has, isEqual } from 'lodash-es';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { ParamInfo, ParamsFormInfo } from '../../models/form.model'; import { ParamInfo, ParamsFormInfo } from '../../models/form.model';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { cleanParams, schemaToParamsFormInfo } from '../../utils/params.utils';
import { ArrayFormComponent } from '../array-form/array-form.component'; import { ArrayFormComponent } from '../array-form/array-form.component';
@Component({ @Component({
selector: 'app-params-form', selector: 'app-params-form',
templateUrl: './params-form.component.html', templateUrl: './params-form.component.html',
@@ -29,7 +29,7 @@ import { ArrayFormComponent } from '../array-form/array-form.component';
], ],
standalone: true, standalone: true,
}) })
export class ParamsFormComponent implements OnInit { export class ParamsFormComponent implements OnChanges, OnDestroy {
@Input() paramsSchema?: SomeJSONSchema; @Input() paramsSchema?: SomeJSONSchema;
@Input() data?: any; @Input() data?: any;
updated = output<any>(); updated = output<any>();
@@ -37,136 +37,75 @@ export class ParamsFormComponent implements OnInit {
paramsFormInfo?: ParamsFormInfo; paramsFormInfo?: ParamsFormInfo;
formGroupSubscription?: Subscription; formGroupSubscription?: Subscription;
paramsOptions: {
display: string;
paramsFormInfo: ParamsFormInfo;
keys: string[];
schema: SomeJSONSchema;
}[] = [];
paramsOptionsSelectedIndex: number = 0;
private schemaService = inject(SchemaService); ngOnDestroy(): void {
console.log('params-form destroyed')
constructor() {} if (this.formGroupSubscription) {
this.formGroupSubscription.unsubscribe()
ngOnInit(): void {
if (this.paramsSchema) {
if (this.paramsSchema.properties) {
this.paramsFormInfo = this.schemaService.getFormInfoFromParamsSchema(this.paramsSchema);
} else if (this.paramsSchema.oneOf) {
this.paramsOptions = this.paramsSchema.oneOf.map((oneOf: any, index: number) => {
const paramsOption = {
display: oneOf.title ? oneOf.title : `Option ${index + 1}`,
schema: oneOf,
paramsFormInfo: this.schemaService.getFormInfoFromParamsSchema(oneOf),
};
return {
...paramsOption,
keys: Object.keys(paramsOption.paramsFormInfo.formGroup.controls),
};
});
const matchingSchemaIndex = this.schemaService.matchParamsDataToSchema(
this.data,
this.paramsOptions.map((paramsOption) => paramsOption.schema),
);
this.paramsOptionsSelectedIndex = matchingSchemaIndex;
this.paramsFormInfo = this.paramsOptions[matchingSchemaIndex].paramsFormInfo;
this.paramsSchema = this.paramsOptions[matchingSchemaIndex].schema;
} else {
console.error('params is not a singular or oneOf');
console.error(this.paramsSchema);
}
} }
if (this.data && this.paramsFormInfo?.formGroup) {
// NOTE(jwetzell): prepare data for form patching
const dataToPatch = cloneDeep(this.data);
Object.entries(this.paramsFormInfo.paramsInfo).forEach(([paramKey, paramInfo]) => {
if (has(dataToPatch, paramKey)) {
switch (paramInfo.type) {
case 'object':
dataToPatch[paramKey] = JSON.stringify(dataToPatch[paramKey]);
break;
case 'array':
dataToPatch[paramKey] = dataToPatch[paramKey]
.map((item: any) => {
switch (typeof item) {
case 'object':
return JSON.stringify(item);
default:
return item;
}
})
.join(',');
break;
default:
break;
}
}
});
this.paramsFormInfo.formGroup.patchValue(dataToPatch);
}
this.formGroupSubscription = this.paramsFormInfo?.formGroup.valueChanges.subscribe((value) => {
this.formUpdated();
});
} }
paramsOptionsTabSelected(event: MatTabChangeEvent) { ngOnChanges(changes: SimpleChanges<{
// NOTE(jwetzell): no longer interested in the old formGroup valueChanges paramsSchema: SomeJSONSchema
if (this.formGroupSubscription) { data: any
this.formGroupSubscription.unsubscribe(); }>): void {
} console.log(changes)
if (changes.paramsSchema) {
const paramsOption = this.paramsOptions[event.index]; if (changes.paramsSchema.previousValue === undefined && changes.paramsSchema.currentValue !== undefined) {
if (this.paramsSchema) {
this.paramsSchema = paramsOption.schema; if (this.paramsSchema.properties) {
this.paramsFormInfo = paramsOption.paramsFormInfo; this.paramsFormInfo = schemaToParamsFormInfo(this.paramsSchema);
if (this.formGroupSubscription === undefined) {
// NOTE(jwetzell): modify params that should change from the data when switch paramOptions this.formGroupSubscription = this.paramsFormInfo?.formGroup.valueChanges.subscribe((value) => {
Object.entries(this.paramsFormInfo.paramsInfo).forEach(([paramKey, paramInfo]) => { this.formUpdated();
if (this.data !== undefined) { });
if (paramInfo.isConst) { }
if (this.data[paramKey]) { } else {
delete this.data[paramKey]; console.error('params is not a singular object');
} console.error(this.paramsSchema);
}
if (paramInfo.default) {
if (this.data[paramKey] !== undefined && this.data[paramKey] !== paramInfo.default) {
this.data[paramKey] = paramInfo.default;
} }
} }
} }
}); }
const allowedParamKeys = Object.keys(this.paramsSchema?.properties); if (changes.data) {
if (this.data) { if (!isEqual(changes.data.currentValue, changes.data.previousValue)) {
// NOTE(jwetzell): remove keys that aren't allowed in the new params variation if (this.paramsFormInfo?.formGroup !== undefined) {
Object.keys(this.data).forEach((paramKey) => { // NOTE(jwetzell): prepare data for form patching
if (allowedParamKeys && !allowedParamKeys.includes(paramKey)) { const dataToPatch = cloneDeep(this.data);
delete this.data[paramKey]; Object.entries(this.paramsFormInfo.paramsInfo).forEach(([paramKey, paramInfo]) => {
if (has(dataToPatch, paramKey)) {
switch (paramInfo.type) {
case 'object':
dataToPatch[paramKey] = JSON.stringify(dataToPatch[paramKey]);
break;
case 'array':
dataToPatch[paramKey] = dataToPatch[paramKey]
.map((item: any) => {
switch (typeof item) {
case 'object':
return JSON.stringify(item);
default:
return item;
}
})
.join(',');
break;
default:
break;
}
}
});
this.paramsFormInfo.formGroup.patchValue(dataToPatch);
} }
}); }
}
if (this.paramsFormInfo.formGroup) {
this.formGroupSubscription = this.paramsFormInfo.formGroup.valueChanges.subscribe((value) => {
this.formUpdated();
});
}
if (this.data && this.paramsFormInfo.formGroup) {
this.paramsFormInfo.formGroup.patchValue(this.data);
} }
} }
formUpdated() { formUpdated() {
if (this.paramsSchema) { if (this.paramsSchema) {
const params = this.schemaService.cleanParams( const params = cleanParams(
this.paramsSchema, this.paramsSchema,
this.paramsFormInfo?.formGroup.value, this.paramsFormInfo?.formGroup.value,
); );
@@ -186,14 +125,16 @@ export class ParamsFormComponent implements OnInit {
getParamInfo(key: string): ParamInfo | undefined { getParamInfo(key: string): ParamInfo | undefined {
return this.paramsFormInfo?.paramsInfo[key]; return this.paramsFormInfo?.paramsInfo[key];
} }
getParamValue(key: string) { getParamValue(key: string) {
if (this.paramsSchema) { if (this.paramsSchema) {
const params = this.schemaService.cleanParams( const params = cleanParams(
this.paramsSchema, this.paramsSchema,
this.paramsFormInfo?.formGroup.value, this.paramsFormInfo?.formGroup.value,
); );
return params[key]; return params[key];
} }
} }
} }
+1 -202
View File
@@ -286,97 +286,7 @@ export class SchemaService {
return 0; return 0;
} }
getFormInfoFromParamsSchema(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,
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(this.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;
}
cleanArray(values: any[], itemSchema: SomeJSONSchema) { cleanArray(values: any[], itemSchema: SomeJSONSchema) {
if (Array.isArray(values)) { if (Array.isArray(values)) {
@@ -397,117 +307,6 @@ export class SchemaService {
return []; return [];
} }
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] = this.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;
}
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;
}
objectValidator(control: AbstractControl): ValidationErrors | null {
try {
JSON.parse(control.value);
return null;
} catch (error) {
return { json: true };
}
}
jsonValidator(validateSchema: SomeJSONSchema) { jsonValidator(validateSchema: SomeJSONSchema) {
return (control: AbstractControl): ValidationErrors | null => { return (control: AbstractControl): ValidationErrors | null => {
try { try {
+206
View File
@@ -0,0 +1,206 @@
import { FormGroup, ValidatorFn, Validators, FormControl, AbstractControl, ValidationErrors } from "@angular/forms";
import { SomeJSONSchema } from "ajv/dist/types/json-schema";
import { ParamsFormInfo } from "../models/form.model";
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,
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;
}