add config upload and viewing returned errors for modules and routes

This commit is contained in:
Joel Wetzell
2026-03-12 17:07:09 -05:00
parent 9570428df9
commit 597e0c4aa4
17 changed files with 248 additions and 142 deletions
+1 -1
View File
@@ -23,7 +23,7 @@
<mat-icon>download</mat-icon> <mat-icon>download</mat-icon>
</button> </button>
@if (configService.pendingConfigIsValid()) { @if (configService.pendingConfigIsValid()) {
<button mat-icon-button matTooltip="Apply Config" (click)="applyConfig()"> <button mat-icon-button matTooltip="Apply Config" (click)="applyConfig()" [disabled]="!configService.configIsDirty()">
<mat-icon>save</mat-icon> <mat-icon>save</mat-icon>
</button> </button>
} @else { } @else {
+2 -2
View File
@@ -46,8 +46,8 @@ export class App {
applyConfig() { applyConfig() {
const config = this.configService.currentlyShownConfig(); const config = this.configService.currentlyShownConfig();
if (config !== undefined) { if (config !== undefined) {
this.configService.saveConfig(config); this.configService.uploadConfig(config);
this.snackBar.open('Config Saved', 'Dismiss', { this.snackBar.open('Config Uploaded', 'Dismiss', {
duration: 3000, duration: 3000,
}); });
} }
@@ -3,7 +3,7 @@ import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { Config, ModuleConfiguration, RouteConfiguration } from '../../models/config.models'; import { Config, ModuleConfig, RouteConfig } from '../../models/config.models';
import { ConfigService } from '../../services/config.service'; import { ConfigService } from '../../services/config.service';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { ModuleListComponent } from '../module-list/module-list.component'; import { ModuleListComponent } from '../module-list/module-list.component';
@@ -42,7 +42,7 @@ export class ConfigComponent {
public eventsService = inject(EventsService); public eventsService = inject(EventsService);
modulesUpdated(modules: ModuleConfiguration[] | undefined) { modulesUpdated(modules: ModuleConfig[] | undefined) {
if (modules === undefined) { if (modules === undefined) {
console.error('modules is undefined, not updating'); console.error('modules is undefined, not updating');
return; return;
@@ -58,7 +58,7 @@ export class ConfigComponent {
}); });
} }
routesUpdated(routes: RouteConfiguration[] | undefined) { routesUpdated(routes: RouteConfig[] | undefined) {
if (routes === undefined) { if (routes === undefined) {
console.error('routes is undefined, not updating'); console.error('routes is undefined, not updating');
return; return;
@@ -1,7 +1,7 @@
import { Component, inject, model } from '@angular/core'; import { Component, inject, model } from '@angular/core';
import { ModuleComponent } from '../module/module.component'; import { ModuleComponent } from '../module/module.component';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { ModuleConfiguration } from '../../models/config.models'; import { ModuleConfig } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
@@ -21,12 +21,12 @@ import { MatTooltipModule } from '@angular/material/tooltip';
styleUrl: './module-list.component.css', styleUrl: './module-list.component.css',
}) })
export class ModuleListComponent { export class ModuleListComponent {
modules = model<ModuleConfiguration[]>(); modules = model<ModuleConfig[]>();
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
private snackBar = inject(MatSnackBar); private snackBar = inject(MatSnackBar);
moduleUpdated(index: number, module: ModuleConfiguration | undefined) { moduleUpdated(index: number, module: ModuleConfig | undefined) {
if (module === undefined) { if (module === undefined) {
console.error('module is undefined, not updating'); console.error('module is undefined, not updating');
return; return;
@@ -43,5 +43,20 @@
</div> </div>
</form> </form>
</div> </div>
<div>
@if (moduleConfigErrors().length > 0) {
<div class="m-2 p-2 border-2 border-red-500">
<div class="flex items-center mb-2 text-red-500">
<mat-icon>error</mat-icon>
<span class="ml-1">Module Errors</span>
</div>
@for (error of moduleConfigErrors(); track error) {
<div class="ml-4 mb-1 text-white">
{{ error.error }}
</div>
}
</div>
}
</div>
</div> </div>
} }
+23 -2
View File
@@ -3,13 +3,14 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
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 { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { ModuleConfiguration } from '../../models/config.models'; import { ModuleConfig } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { ParamsFormComponent } from '../params-form/params-form.component'; import { ParamsFormComponent } from '../params-form/params-form.component';
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { EventsService } from '../../services/events.service'; import { EventsService } from '../../services/events.service';
import { tap, debounceTime } from 'rxjs'; import { tap, debounceTime } from 'rxjs';
import { ConfigService } from '../../services/config.service';
@Component({ @Component({
selector: 'app-module', selector: 'app-module',
@@ -27,7 +28,17 @@ import { tap, debounceTime } from 'rxjs';
}) })
export class ModuleComponent { export class ModuleComponent {
path = input<string>(''); path = input<string>('');
module = model<ModuleConfiguration>(); index = computed(() => {
const path = this.path();
if (path) {
const parts = path.split('/');
const lastPart = parts[parts.length - 1];
return parseInt(lastPart, 10);
}
return undefined;
});
module = model<ModuleConfig>();
delete = output<void>(); delete = output<void>();
params = computed(() => this.module()!.params); params = computed(() => this.module()!.params);
@@ -39,6 +50,15 @@ export class ModuleComponent {
: undefined; : undefined;
}); });
moduleConfigErrors = computed(() => {
const index = this.index();
const moduleErrors = this.configService.moduleErrors();
if (index !== undefined) {
return moduleErrors.filter((error) => error.index === index);
}
return [];
});
formGroup: FormGroup = new FormGroup({ formGroup: FormGroup = new FormGroup({
id: new FormControl('', [Validators.required]), id: new FormControl('', [Validators.required]),
type: new FormControl(''), type: new FormControl(''),
@@ -49,6 +69,7 @@ export class ModuleComponent {
private schemaService = inject(SchemaService); private schemaService = inject(SchemaService);
private eventsService = inject(EventsService); private eventsService = inject(EventsService);
private configService = inject(ConfigService);
ngOnInit(): void { ngOnInit(): void {
this.formGroup.patchValue({ this.formGroup.patchValue({
id: this.module()?.id, id: this.module()?.id,
@@ -9,7 +9,7 @@
<div class="flex-grow ml-1 mr-3 text-white"> <div class="flex-grow ml-1 mr-3 text-white">
{{ schema()?.title || processor()?.type }} {{ schema()?.title || processor()?.type }}
</div> </div>
<div class="flex items-center justify-center" (click)="delete.emit()"> <div class="flex items-center justify-center hover:bg-gray-700" (click)="delete.emit()">
<mat-icon class="!text-red-500">close</mat-icon> <mat-icon class="!text-red-500">close</mat-icon>
</div> </div>
</div> </div>
@@ -1,5 +1,5 @@
import { Component, computed, inject, input, model, output, signal } from '@angular/core'; import { Component, computed, inject, input, model, output, signal } from '@angular/core';
import { ProcessorConfiguration } from '../../models/config.models'; import { ProcessorConfig } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { ParamsFormComponent } from '../params-form/params-form.component'; import { ParamsFormComponent } from '../params-form/params-form.component';
@@ -13,7 +13,7 @@ import { ReactiveFormsModule } from '@angular/forms';
}) })
export class ProcessorComponent { export class ProcessorComponent {
path = input<string>(''); path = input<string>('');
processor = model<ProcessorConfiguration>(); processor = model<ProcessorConfig>();
delete = output<void>(); delete = output<void>();
params = computed(() => this.processor()!.params); params = computed(() => this.processor()!.params);
@@ -1,6 +1,6 @@
import { Component, inject, input, model } from '@angular/core'; import { Component, inject, input, model } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { ModuleConfiguration, RouteConfiguration } from '../../models/config.models'; import { ModuleConfig, RouteConfig } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { RouteComponent } from '../route/route.component'; import { RouteComponent } from '../route/route.component';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
@@ -22,7 +22,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
}) })
export class RouteListComponent { export class RouteListComponent {
routes = model<RouteConfiguration[]>(); routes = model<RouteConfig[]>();
moduleIds = input<string[]>(); moduleIds = input<string[]>();
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
@@ -41,7 +41,7 @@ export class RouteListComponent {
}); });
} }
routeUpdated(index: number, route: RouteConfiguration | undefined) { routeUpdated(index: number, route: RouteConfig | undefined) {
if (route === undefined) { if (route === undefined) {
console.error('route is undefined, not updating'); console.error('route is undefined, not updating');
return; return;
@@ -49,7 +49,6 @@ export class RouteListComponent {
this.routes.update((routes) => { this.routes.update((routes) => {
if (routes) { if (routes) {
routes[index].input = route.input; routes[index].input = route.input;
routes[index].output = route.output;
return [...routes]; return [...routes];
} }
return routes; return routes;
@@ -73,6 +73,21 @@
} }
</div> </div>
</div> </div>
<div>
@if (routeConfigErrors().length > 0) {
<div class="m-2 p-2 border-2 border-red-500">
<div class="flex items-center mb-2 text-red-500">
<mat-icon>error</mat-icon>
<span class="ml-1">Route Errors</span>
</div>
@for (error of routeConfigErrors(); track error) {
<div class="ml-4 mb-1 text-white">
{{ error.error }}
</div>
}
</div>
}
</div>
</div> </div>
<!-- </div> --> <!-- </div> -->
+14 -3
View File
@@ -3,7 +3,7 @@ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angula
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 { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { ProcessorConfiguration, RouteConfiguration } from '../../models/config.models'; import { ProcessorConfig, RouteConfig } from '../../models/config.models';
import { SchemaService } from '../../services/schema.service'; import { SchemaService } from '../../services/schema.service';
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { ProcessorComponent } from '../processor/processor.component'; import { ProcessorComponent } from '../processor/processor.component';
@@ -11,6 +11,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { EventsService } from '../../services/events.service'; import { EventsService } from '../../services/events.service';
import { debounceTime, tap } from 'rxjs'; import { debounceTime, tap } from 'rxjs';
import { ConfigService } from '../../services/config.service';
@Component({ @Component({
selector: 'app-route', selector: 'app-route',
@@ -39,7 +40,7 @@ export class RouteComponent {
return undefined; return undefined;
}); });
route = model<RouteConfiguration>(); route = model<RouteConfig>();
moduleIds = input<string[]>([]); moduleIds = input<string[]>([]);
delete = output<void>(); delete = output<void>();
@@ -51,6 +52,15 @@ export class RouteComponent {
return []; return [];
}); });
routeConfigErrors = computed(() => {
const index = this.index();
const routeErrors = this.configService.routeErrors();
if (index !== undefined) {
return routeErrors.filter((error) => error.index === index);
}
return [];
});
formGroup: FormGroup = new FormGroup({ formGroup: FormGroup = new FormGroup({
input: new FormControl('', [Validators.required]), input: new FormControl('', [Validators.required]),
}); });
@@ -58,6 +68,7 @@ export class RouteComponent {
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
private snackBar = inject(MatSnackBar); private snackBar = inject(MatSnackBar);
private eventsService = inject(EventsService); private eventsService = inject(EventsService);
private configService = inject(ConfigService);
indicatorColor = signal<string>('gray'); indicatorColor = signal<string>('gray');
@@ -119,7 +130,7 @@ export class RouteComponent {
}); });
} }
processorUpdated(index: number, processor: ProcessorConfiguration | undefined) { processorUpdated(index: number, processor: ProcessorConfig | undefined) {
if (processor === undefined) { if (processor === undefined) {
console.error('processor is undefined, not updating'); console.error('processor is undefined, not updating');
return; return;
+29 -7
View File
@@ -1,6 +1,7 @@
export type Config = { export type Config = {
modules: ModuleConfiguration[]; api: ApiConfig
routes: RouteConfiguration[]; modules: ModuleConfig[];
routes: RouteConfig[];
}; };
export type ConfigState = { export type ConfigState = {
@@ -9,19 +10,40 @@ export type ConfigState = {
isCurrent: boolean; isCurrent: boolean;
}; };
export type ModuleConfiguration = { export type ApiConfig = {
port: number;
};
export type ModuleConfig = {
id?: string; id?: string;
type: string; type: string;
params?: Record<string, any>; params?: Record<string, any>;
}; };
export type RouteConfiguration = { export type RouteConfig = {
input?: string; input?: string;
processors?: ProcessorConfiguration[]; processors?: ProcessorConfig[];
output?: string;
}; };
export type ProcessorConfiguration = { export type ProcessorConfig = {
type: string; type: string;
params?: Record<string, any>; params?: Record<string, any>;
}; };
export type ModuleError = {
index: number;
config: ModuleConfig;
error: string;
}
export type RouteError = {
index: number;
config: RouteConfig;
error: string;
}
export type ConfigError = {
moduleErrors?: ModuleError[];
routeErrors?: RouteError[];
}
+1
View File
@@ -1,3 +1,4 @@
import { Config } from "./config.models"
export type RouterEvent<T, D> = { export type RouterEvent<T, D> = {
type: T, type: T,
+48 -27
View File
@@ -1,9 +1,10 @@
import { computed, effect, inject, Injectable, signal } from '@angular/core'; import { computed, effect, inject, Injectable, signal } from '@angular/core';
import { cloneDeep } from 'lodash-es'; import { cloneDeep, isEqual } from 'lodash-es';
import { Config } from '../models/config.models'; import { Config, ConfigError, ModuleError, RouteError } from '../models/config.models';
import { SchemaService } from './schema.service'; import { SchemaService } from './schema.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { SettingsService } from './settings.service'; import { SettingsService } from './settings.service';
import { EventsService } from './events.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
@@ -16,6 +17,19 @@ export class ConfigService {
return this.schemaService.validate(config); return this.schemaService.validate(config);
}); });
currentlyShownConfig = signal<Config | undefined>(undefined); currentlyShownConfig = signal<Config | undefined>(undefined);
runningConfig = signal<Config | undefined>(undefined);
configIsDirty = computed(() => {
const currentlyShown = this.currentlyShownConfig();
const running = this.runningConfig();
if (currentlyShown === undefined || running === undefined) {
return false;
}
return !isEqual(currentlyShown, running);
});
moduleErrors = signal<ModuleError[]>([]);
routeErrors = signal<RouteError[]>([]);
private http = inject(HttpClient); private http = inject(HttpClient);
private settingsService = inject(SettingsService); private settingsService = inject(SettingsService);
@@ -28,25 +42,6 @@ export class ConfigService {
} }
loadConfig() { loadConfig() {
// const configString = localStorage.getItem('config');
// if (configString) {
// try {
// const config = JSON.parse(configString);
// const valid = this.schemaService.validate(config);
// if (valid) {
// console.log('Loaded config from local storage', config);
// this.updateCurrentlyShownConfig(config);
// } else {
// console.error('Config in local storage is invalid', config);
// this.setEmptyConfig();
// }
// } catch (e) {
// console.error('Failed to parse config from local storage', e);
// this.setEmptyConfig();
// }
// } else {
// this.setEmptyConfig();
// }
const configUrl = this.settingsService.configUrl(); const configUrl = this.settingsService.configUrl();
if (!configUrl) { if (!configUrl) {
console.error('Config URL is not set'); console.error('Config URL is not set');
@@ -57,6 +52,7 @@ export class ConfigService {
next: (config) => { next: (config) => {
if (this.schemaService.validate(config)) { if (this.schemaService.validate(config)) {
this.updateCurrentlyShownConfig(config); this.updateCurrentlyShownConfig(config);
this.runningConfig.set(cloneDeep(config));
} else { } else {
console.error('Config from server is invalid', config); console.error('Config from server is invalid', config);
this.setEmptyConfig(); this.setEmptyConfig();
@@ -69,21 +65,46 @@ export class ConfigService {
}); });
} }
uploadConfig(config: Config) {
if (this.schemaService.validate(config)) {
this.updateCurrentlyShownConfig(config);
const configUrl = this.settingsService.configUrl();
if (!configUrl) {
console.error('Config URL is not set, cannot upload config');
return;
}
this.http.put<ConfigError>(configUrl.toString(), config).subscribe({
next: () => {
console.log('Config uploaded successfully');
this.moduleErrors.set([]);
this.routeErrors.set([]);
this.runningConfig.set(cloneDeep(config));
},
error: (err) => {
console.error('Problems occurred while uploading config', err);
if (err.error) {
const configError: ConfigError = err.error;
this.moduleErrors.set(configError.moduleErrors ?? []);
this.routeErrors.set(configError.routeErrors ?? []);
}
this.runningConfig.set(cloneDeep(config));
},
});
} else {
console.error('Uploaded config is invalid', config);
}
}
setEmptyConfig() { setEmptyConfig() {
console.log('Setting empty config'); console.log('Setting empty config');
this.updateCurrentlyShownConfig({ this.updateCurrentlyShownConfig({
api: { port: 8080 },
modules: [], modules: [],
routes: [], routes: [],
}); });
} }
saveConfig(config: Config) {
localStorage.setItem('config', JSON.stringify(config));
console.log('Config saved to local storage', config);
}
updateCurrentlyShownConfig(config: Config) { updateCurrentlyShownConfig(config: Config) {
// NOTE(jwetzell): mark the configStates that match the currently shown as such
this.currentlyShownConfig.set(cloneDeep(config)); this.currentlyShownConfig.set(cloneDeep(config));
} }
} }
+21 -19
View File
@@ -1,18 +1,21 @@
import { effect, inject, Injectable, signal } from '@angular/core'; import { effect, inject, Injectable, signal } from '@angular/core';
import { InputEventData, OutputEventData, RouteEventData, RouterEvent } from '../models/events.models'; import {
InputEventData,
OutputEventData,
RouteEventData,
RouterEvent,
} from '../models/events.models';
import { filter, Subject } from 'rxjs'; import { filter, Subject } from 'rxjs';
import { SettingsService } from './settings.service'; import { SettingsService } from './settings.service';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class EventsService { export class EventsService {
status = signal<string>('closed'); status = signal<string>('closed');
socket?: WebSocket; socket?: WebSocket;
private routeEvents$ = new Subject<RouterEvent<'route', RouteEventData>>(); private routeEvents$ = new Subject<RouterEvent<'route', RouteEventData>>();
private inputEvents$ = new Subject<RouterEvent<'input', InputEventData>>(); private inputEvents$ = new Subject<RouterEvent<'input', InputEventData>>();
private outputEvents$ = new Subject<RouterEvent<'output', OutputEventData>>(); private outputEvents$ = new Subject<RouterEvent<'output', OutputEventData>>();
private settingsService = inject(SettingsService); private settingsService = inject(SettingsService);
constructor() { constructor() {
@@ -27,18 +30,15 @@ export class EventsService {
this.socket = new WebSocket(this.settingsService.wsUrl()); this.socket = new WebSocket(this.settingsService.wsUrl());
this.socket.onopen = () => { this.socket.onopen = () => {
this.status.set('open'); this.status.set('open');
} };
this.socket.onclose = () => { this.socket.onclose = () => {
this.status.set('closed'); this.status.set('closed');
setTimeout(() => { };
this.reload();
}, 2000);
}
this.socket.onerror = (error) => { this.socket.onerror = (error) => {
this.status.set('error'); this.status.set('error');
} };
this.socket.onmessage = (event) => { this.socket.onmessage = (event) => {
const messageObj = JSON.parse(event.data); const messageObj = JSON.parse(event.data);
@@ -55,28 +55,30 @@ export class EventsService {
default: default:
console.warn('Unknown event type:', messageObj.type); console.warn('Unknown event type:', messageObj.type);
} }
} };
} catch (error) { } catch (error) {
console.error('Failed to connect to WebSocket:', error); console.error('Failed to connect to WebSocket:', error);
} }
} }
getRouteEventsForIndex(index: number) { getRouteEventsForIndex(index: number) {
return this.routeEvents$.pipe( return this.routeEvents$.pipe(filter((event) => event.data?.index === index));
filter(event => event.data?.index === index)
);
} }
getInputEventsForSource(source: string) { getInputEventsForSource(source: string) {
return this.inputEvents$.pipe( return this.inputEvents$.pipe(filter((event) => event.data?.source === source));
filter(event => event.data?.source === source)
);
} }
getOutputEventsForDestination(destination: string) { getOutputEventsForDestination(destination: string) {
return this.outputEvents$.pipe( return this.outputEvents$.pipe(filter((event) => event.data?.destination === destination));
filter(event => event.data?.destination === destination)
);
} }
sendEvent<T, D>(event: RouterEvent<T, D>) {
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
console.log('sending event:', event);
this.socket.send(JSON.stringify(event));
} else {
console.warn('WebSocket is not open. Cannot send event:', event);
}
}
} }
+16 -17
View File
@@ -11,19 +11,18 @@ import {
import { Ajv2020, JSONSchemaType } from 'ajv/dist/2020'; import { Ajv2020, JSONSchemaType } from 'ajv/dist/2020';
import { SomeJSONSchema } from 'ajv/dist/types/json-schema'; import { SomeJSONSchema } from 'ajv/dist/types/json-schema';
import { sortBy } from 'lodash-es'; import { sortBy } from 'lodash-es';
import { Config, ModuleConfiguration, ProcessorConfiguration } from '../models/config.models'; import { Config, ModuleConfig, ProcessorConfig, RouteConfig } from '../models/config.models';
import { ObjectInfo, ParamsFormInfo } from '../models/form.model'; import { ObjectInfo, ParamsFormInfo } from '../models/form.model';
import { SettingsService } from './settings.service'; import { SettingsService } from './settings.service';
import { RouteConfiguration } from '../models/config.models';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class SchemaService { export class SchemaService {
configSchema = signal<JSONSchemaType<Config> | undefined>(undefined); configSchema = signal<JSONSchemaType<Config> | undefined>(undefined);
modulesSchema = signal<JSONSchemaType<ModuleConfiguration[]> | undefined>(undefined); modulesSchema = signal<JSONSchemaType<ModuleConfig[]> | undefined>(undefined);
routesSchema = signal<JSONSchemaType<RouteConfiguration[]> | undefined>(undefined); routesSchema = signal<JSONSchemaType<RouteConfig[]> | undefined>(undefined);
processorsSchema = signal<JSONSchemaType<ProcessorConfiguration[]> | undefined>(undefined); processorsSchema = signal<JSONSchemaType<ProcessorConfig[]> | undefined>(undefined);
schemasLoaded = computed(() => { schemasLoaded = computed(() => {
return ( return (
@@ -90,7 +89,7 @@ export class SchemaService {
loadModulesSchema() { loadModulesSchema() {
this.http this.http
.get< .get<
JSONSchemaType<ModuleConfiguration[]> JSONSchemaType<ModuleConfig[]>
>(this.settingsService.modulesSchemaUrl().toString()) >(this.settingsService.modulesSchemaUrl().toString())
.subscribe((schema) => { .subscribe((schema) => {
this.setModulesSchema(schema); this.setModulesSchema(schema);
@@ -99,7 +98,7 @@ export class SchemaService {
loadRoutesSchema() { loadRoutesSchema() {
this.http this.http
.get<JSONSchemaType<RouteConfiguration[]>>(this.settingsService.routesSchemaUrl().toString()) .get<JSONSchemaType<RouteConfig[]>>(this.settingsService.routesSchemaUrl().toString())
.subscribe((schema) => { .subscribe((schema) => {
this.setRoutesSchema(schema); this.setRoutesSchema(schema);
}); });
@@ -108,7 +107,7 @@ export class SchemaService {
loadProcessorsSchema() { loadProcessorsSchema() {
this.http this.http
.get< .get<
JSONSchemaType<ProcessorConfiguration[]> JSONSchemaType<ProcessorConfig[]>
>(this.settingsService.processorsSchemaUrl().toString()) >(this.settingsService.processorsSchemaUrl().toString())
.subscribe((schema) => { .subscribe((schema) => {
this.setProcessorsSchema(schema); this.setProcessorsSchema(schema);
@@ -142,13 +141,13 @@ export class SchemaService {
return true; return true;
} }
getSkeletonForRoute(): RouteConfiguration { getSkeletonForRoute(): RouteConfig {
const template: RouteConfiguration = {}; const template: RouteConfig = {};
return template; return template;
} }
getSkeletonForProcessor(processorType: string): ProcessorConfiguration { getSkeletonForProcessor(processorType: string): ProcessorConfig {
const template: ProcessorConfiguration = { const template: ProcessorConfig = {
type: processorType, type: processorType,
}; };
const itemInfo = this.processorTypes.find((itemInfo) => itemInfo.type === processorType); const itemInfo = this.processorTypes.find((itemInfo) => itemInfo.type === processorType);
@@ -160,8 +159,8 @@ export class SchemaService {
return template; return template;
} }
getSkeletonForModule(moduleType: string): ModuleConfiguration { getSkeletonForModule(moduleType: string): ModuleConfig {
const template: ModuleConfiguration = { const template: ModuleConfig = {
type: moduleType, type: moduleType,
}; };
const itemInfo = this.moduleTypes.find((itemInfo) => itemInfo.type === moduleType); const itemInfo = this.moduleTypes.find((itemInfo) => itemInfo.type === moduleType);
@@ -198,17 +197,17 @@ export class SchemaService {
this.configSchema.set(schema); this.configSchema.set(schema);
} }
setModulesSchema(schema: JSONSchemaType<ModuleConfiguration[]>) { setModulesSchema(schema: JSONSchemaType<ModuleConfig[]>) {
this.modulesSchema.set(schema); this.modulesSchema.set(schema);
this.moduleTypes = []; this.moduleTypes = [];
this.populateModuleTypes(); this.populateModuleTypes();
} }
setRoutesSchema(schema: JSONSchemaType<RouteConfiguration[]>) { setRoutesSchema(schema: JSONSchemaType<RouteConfig[]>) {
this.routesSchema.set(schema); this.routesSchema.set(schema);
} }
setProcessorsSchema(schema: JSONSchemaType<ProcessorConfiguration[]>) { setProcessorsSchema(schema: JSONSchemaType<ProcessorConfig[]>) {
this.processorsSchema.set(schema); this.processorsSchema.set(schema);
this.processorTypes = []; this.processorTypes = [];
this.populateProcessorTypes(); this.populateProcessorTypes();
+4 -4
View File
@@ -6,10 +6,10 @@ import { computed, Injectable, signal } from '@angular/core';
export class SettingsService { export class SettingsService {
public isDummySite: boolean = false; public isDummySite: boolean = false;
public configPath = signal('/api/v1/config'); public configPath = signal('/api/v1/config');
public configSchemaPath = signal('/api/v1/schema/config'); public configSchemaPath = signal('/schema/config');
public modulesSchemaPath = signal('/api/v1/schema/modules'); public modulesSchemaPath = signal('/schema/modules');
public routesSchemaPath = signal('/api/v1/schema/routes'); public routesSchemaPath = signal('/schema/routes');
public processorsSchemaPath = signal('/api/v1/schema/processors'); public processorsSchemaPath = signal('/schema/processors');
public wsPath = signal('/ws'); public wsPath = signal('/ws');
public baseUrl = signal('http://localhost:8080'); public baseUrl = signal('http://localhost:8080');