From 998d3adce6e7c2fab0cef0fe442b9fb6f27fee26 Mon Sep 17 00:00:00 2001 From: Joel Wetzell Date: Wed, 6 May 2026 16:56:32 -0500 Subject: [PATCH] fix schema loading race --- src/app/services/config.ts | 8 ++++---- src/app/services/schema.ts | 40 +++++++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/app/services/config.ts b/src/app/services/config.ts index 6212f39..6db29e5 100644 --- a/src/app/services/config.ts +++ b/src/app/services/config.ts @@ -1,6 +1,6 @@ import { computed, effect, inject, Injectable, signal } from '@angular/core'; import { cloneDeep, isEqual } from 'lodash-es'; -import { Config, ConfigError, ModuleError, RouteError } from '../models/config'; +import { Config, ConfigError, ModuleConfig, ModuleError, RouteConfig, RouteError } from '../models/config'; import { SchemaService } from './schema'; import { HttpClient } from '@angular/common/http'; import { SettingsService } from './settings'; @@ -14,7 +14,7 @@ export class ConfigService { if (config === undefined) { return false; } - return this.schemaService.validate(config); + return this.schemaService.validate(this.schemaService.configSchemaId, config); }); currentlyShownConfig = signal(undefined); runningConfig = signal(undefined); @@ -59,7 +59,7 @@ export class ConfigService { } this.http.get(configUrl.toString()).subscribe({ next: (config) => { - if (this.schemaService.validate(config)) { + if (this.schemaService.validate(this.schemaService.configSchemaId, config)) { this.updateCurrentlyShownConfig(config); this.runningConfig.set(cloneDeep(config)); } else { @@ -75,7 +75,7 @@ export class ConfigService { } uploadConfig(config: Config) { - if (this.schemaService.validate(config)) { + if (this.schemaService.validate(this.schemaService.configSchemaId,config)) { this.updateCurrentlyShownConfig(config); const configUrl = this.settingsService.configUrl(); if (!configUrl) { diff --git a/src/app/services/schema.ts b/src/app/services/schema.ts index 7420dee..44905c9 100644 --- a/src/app/services/schema.ts +++ b/src/app/services/schema.ts @@ -25,6 +25,12 @@ export class SchemaService { routesSchema = signal | undefined>(undefined); processorsSchema = signal | undefined>(undefined); + // TODO(jwetzell): populate from loaded schemas + configSchemaId = 'https://showbridge.io/config.schema.json'; + modulesSchemaId = 'https://showbridge.io/modules.schema.json'; + routesSchemaId = 'https://showbridge.io/routes.schema.json'; + processorsSchemaId = 'https://showbridge.io/processors.schema.json'; + schemasLoaded = computed(() => { return ( this.configSchema() !== undefined && @@ -63,10 +69,7 @@ export class SchemaService { } break; case 'closed': - this.ajv.removeSchema(this.configSchema()!); - this.ajv.removeSchema(this.modulesSchema()!); - this.ajv.removeSchema(this.routesSchema()!); - this.ajv.removeSchema(this.processorsSchema()!); + this.resetAjv(); this.configSchema.set(undefined); this.modulesSchema.set(undefined); this.routesSchema.set(undefined); @@ -74,15 +77,13 @@ export class SchemaService { break; } }); + } - effect(() => { - if (this.schemasLoaded()) { - this.ajv.addSchema(this.modulesSchema()!); - this.ajv.addSchema(this.routesSchema()!); - this.ajv.addSchema(this.processorsSchema()!); - this.ajv.compile(this.configSchema()!); - } - }); + resetAjv() { + this.ajv.removeSchema(this.configSchema()!); + this.ajv.removeSchema(this.modulesSchema()!); + this.ajv.removeSchema(this.routesSchema()!); + this.ajv.removeSchema(this.processorsSchema()!); } loadConfigSchema() { @@ -117,9 +118,9 @@ export class SchemaService { }); } - validate(data: any): boolean { + validate(schemaId: string, data: any): boolean { if (this.schemasLoaded() && this.ajv) { - this.ajv.validate('https://showbridge.io/config.schema.json', data); + this.ajv.validate(schemaId, data); if (this.ajv.errors) { console.error('validation errors', this.ajv.errors); const errorPaths = new Set( @@ -196,21 +197,32 @@ export class SchemaService { return paramsTemplate; } + addSchemaToAjv(schema: SomeJSONSchema) { + if (this.ajv.getSchema(schema.$id!)) { + this.ajv.removeSchema(schema.$id!); + } + this.ajv.addSchema(schema); + } + setConfigSchema(schema: JSONSchemaType) { + this.addSchemaToAjv(schema); this.configSchema.set(schema); } setModulesSchema(schema: JSONSchemaType) { + this.addSchemaToAjv(schema); this.modulesSchema.set(schema); this.moduleTypes = []; this.populateModuleTypes(); } setRoutesSchema(schema: JSONSchemaType) { + this.addSchemaToAjv(schema); this.routesSchema.set(schema); } setProcessorsSchema(schema: JSONSchemaType) { + this.addSchemaToAjv(schema); this.processorsSchema.set(schema); this.processorTypes = []; this.populateProcessorTypes();