formatting

This commit is contained in:
Joel Wetzell
2026-04-05 18:09:47 -05:00
parent 10ff26e48c
commit e66195d702
24 changed files with 248 additions and 258 deletions
+3 -6
View File
@@ -1,15 +1,12 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
test.beforeEach(async ({page})=>{ test.beforeEach(async ({ page }) => {
await page.goto('/') await page.goto('/');
await expect(page.locator('div').nth(2)).toContainClass('bg-green-400'); await expect(page.locator('div').nth(2)).toContainClass('bg-green-400');
}) });
test('has title', async ({ page }) => { test('has title', async ({ page }) => {
await page.goto('/'); await page.goto('/');
// Expect a title "to contain" a substring. // Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Showbridge/); await expect(page).toHaveTitle(/Showbridge/);
}); });
+5 -5
View File
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test' import { expect, test } from '@playwright/test';
test('add module', async ({ page }) => { test('add module', async ({ page }) => {
await page.goto('/'); await page.goto('/');
await page.locator('app-module-list').getByText('add').click(); await page.locator('app-module-list').getByText('add').click();
await page.getByRole('menuitem', { name: 'HTTP Server' }).click(); await page.getByRole('menuitem', { name: 'HTTP Server' }).click();
}) });
+5 -5
View File
@@ -23,11 +23,11 @@ export default defineConfig({
reporter: 'html', reporter: 'html',
webServer: [ webServer: [
{ {
name: "showbridge", name: 'showbridge',
command: "showbridge --config config.yaml", command: 'showbridge --config config.yaml',
url: "http://localhost:8080/health", url: 'http://localhost:8080/health',
timeout: 10000 timeout: 10000,
} },
], ],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: { use: {
+1 -3
View File
@@ -1,7 +1,5 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
export const appConfig: ApplicationConfig = { export const appConfig: ApplicationConfig = {
providers: [ providers: [provideBrowserGlobalErrorListeners()],
provideBrowserGlobalErrorListeners(),
],
}; };
+25 -13
View File
@@ -1,7 +1,7 @@
<div class="h-screen max-h-screen flex flex-col"> <div class="h-screen max-h-screen flex flex-col">
<div class="w-full"> <div class="w-full">
<mat-toolbar color="primary"> <mat-toolbar color="primary">
@if(eventsService.status() === 'open') { @if (eventsService.status() === 'open') {
<div matTooltip="Connected" class="h-3 w-3 rounded-full bg-green-400 mr-2"></div> <div matTooltip="Connected" class="h-3 w-3 rounded-full bg-green-400 mr-2"></div>
} @else if (eventsService.status() === 'closed') { } @else if (eventsService.status() === 'closed') {
<div matTooltip="Disconnected" class="h-3 w-3 rounded-full bg-orange-400 mr-2"></div> <div matTooltip="Disconnected" class="h-3 w-3 rounded-full bg-orange-400 mr-2"></div>
@@ -10,18 +10,23 @@
} }
<span>showbridge</span> <span>showbridge</span>
<div> <div>
<label class="ml-4 mr-2">URL:</label> <label class="ml-4 mr-2">URL:</label>
<input <input
type="text" type="text"
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm" class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[placeholder]="'http://localhost:8000'" [placeholder]="'http://localhost:8000'"
[value]="settingsService.baseUrl()" [value]="settingsService.baseUrl()"
(change)="settingsService.updateBaseUrl($event.target.value)" (change)="settingsService.updateBaseUrl($event.target.value)"
/> />
</div> </div>
<span class="flex-auto"></span> <span class="flex-auto"></span>
@if (configService.pendingConfigIsValid()) { @if (configService.pendingConfigIsValid()) {
<button mat-icon-button matTooltip="Apply Config" (click)="applyConfig()" [disabled]="!configService.configIsDirty()"> <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 {
@@ -34,11 +39,18 @@
} }
</mat-toolbar> </mat-toolbar>
</div> </div>
@if (eventsService.status() === 'open' && schemaService.schemasLoaded()) { @if (eventsService.status() === 'open' && schemaService.schemasLoaded()) {
<div class="grow overflow-hidden flex m-2"> <div class="grow overflow-hidden flex m-2">
<div class="grow overflow-y-auto"> <div class="grow overflow-y-auto">
<app-module-list [modules]="config()?.modules" (modulesChange)="modulesUpdated($event)"></app-module-list> <app-module-list
<app-route-list [routes]="config()?.routes" [moduleIds]="moduleIds()" (routesChange)="routesUpdated($event)"></app-route-list> [modules]="config()?.modules"
(modulesChange)="modulesUpdated($event)"
></app-module-list>
<app-route-list
[routes]="config()?.routes"
[moduleIds]="moduleIds()"
(routesChange)="routesUpdated($event)"
></app-route-list>
</div> </div>
<div class="w-1/2 overflow-y-hidden"> <div class="w-1/2 overflow-y-hidden">
<app-config-preview [config]="config()"></app-config-preview> <app-config-preview [config]="config()"></app-config-preview>
+3 -4
View File
@@ -28,13 +28,12 @@ import { ConfigPreviewComponent } from './components/config-preview/config-previ
MatTooltipModule, MatTooltipModule,
ModuleListComponent, ModuleListComponent,
RouteListComponent, RouteListComponent,
ConfigPreviewComponent ConfigPreviewComponent,
], ],
templateUrl: './app.html', templateUrl: './app.html',
styleUrl: './app.css', styleUrl: './app.css',
}) })
export class App { export class App {
config = computed<Config | undefined>(() => this.configService.currentlyShownConfig()); config = computed<Config | undefined>(() => this.configService.currentlyShownConfig());
modules = computed(() => this.config()?.modules ?? []); modules = computed(() => this.config()?.modules ?? []);
@@ -47,7 +46,7 @@ export class App {
} }
return []; return [];
}); });
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
public configService = inject(ConfigService); public configService = inject(ConfigService);
+2 -5
View File
@@ -31,10 +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 = parseStringToArray( this.arrayValue = parseStringToArray(this.paramFormControl.value, this.paramInfo.schema);
this.paramFormControl.value,
this.paramInfo.schema,
);
} else { } else {
this.arrayValue = this.paramFormControl.value; this.arrayValue = this.paramFormControl.value;
} }
@@ -101,7 +98,7 @@ export class ArrayFormComponent implements OnInit {
); );
} }
} }
// NOTE(jwetzel): this is only needed for object item types // NOTE(jwetzel): this is only needed for object item types
updateItem(index: number, value: any) { updateItem(index: number, value: any) {
if (this.arrayValue) { if (this.arrayValue) {
@@ -2,17 +2,13 @@ import { JsonPipe } from '@angular/common';
import { Component, computed, input } from '@angular/core'; import { Component, computed, input } from '@angular/core';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
import { Config } from '../../models/config'; import { Config } from '../../models/config';
import { MatIcon, MatIconModule } from "@angular/material/icon"; import { MatIcon, MatIconModule } from '@angular/material/icon';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
@Component({ @Component({
selector: 'app-config-preview', selector: 'app-config-preview',
imports: [ imports: [MatIconModule, MatButtonModule, MatTooltipModule],
MatIconModule,
MatButtonModule,
MatTooltipModule
],
templateUrl: './config-preview.html', templateUrl: './config-preview.html',
styleUrl: './config-preview.css', styleUrl: './config-preview.css',
}) })
@@ -10,13 +10,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
@Component({ @Component({
selector: 'app-module-list', selector: 'app-module-list',
imports: [ imports: [MatMenuModule, MatIconModule, MatButtonModule, MatTooltipModule, ModuleComponent],
MatMenuModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
ModuleComponent
],
templateUrl: './module-list.html', templateUrl: './module-list.html',
styleUrl: './module-list.css', styleUrl: './module-list.css',
}) })
+51 -44
View File
@@ -1,49 +1,56 @@
@if (module() && schema()) { @if (module() && schema()) {
<div <div
class="flex flex-col bg-gray-800 border-2 {{ class="flex flex-col bg-gray-800 border-2 {{
isInError() ? 'border-red-500' : 'border-gray-600' isInError() ? 'border-red-500' : 'border-gray-600'
}} border-solid w-full h-full" }} border-solid w-full h-full"
> >
<div class="flex items-center justify-center"> <div class="flex items-center justify-center">
<div class="flex items-center"> <div class="flex items-center">
<mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator">keyboard_arrow_down</mat-icon> <mat-icon [style.color]="inputIndicatorColor()" matTooltip="Input Indicator"
<mat-icon [style.color]="outputIndicatorColor()" matTooltip="Output Indicator">keyboard_arrow_up</mat-icon> >keyboard_arrow_down</mat-icon
</div> >
<div class="text-white"> <mat-icon [style.color]="outputIndicatorColor()" matTooltip="Output Indicator"
{{ schema().title || module()?.type }} >keyboard_arrow_up</mat-icon
</div> >
<div class="grow"></div>
<div class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer" (click)="delete.emit()">
<mat-icon class="text-red-500!">close</mat-icon>
</div>
</div> </div>
<hr class="bg-gray-600 h-0.5 border-0"> <div class="text-white">
<div> {{ schema().title || module()?.type }}
<form [formGroup]="formGroup" class="h-full"> </div>
<div class="m-2"> <div class="grow"></div>
<div class="flex items-center m-1"> <div
<label class="mr-2 text-white"> ID: </label> class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
<input (click)="delete.emit()"
type="text" >
class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm" <mat-icon class="text-red-500!">close</mat-icon>
[placeholder]="'module-1'" </div>
[formControlName]="'id'" </div>
/> <hr class="bg-gray-600 h-0.5 border-0" />
<mat-icon class="text-white!" matTooltip="String">abc</mat-icon> <div>
@if (!formGroup.controls['id'].valid) { <form [formGroup]="formGroup" class="h-full">
<!-- TODO(jwetzell): better error displaying --> <div class="m-2">
<div class="ml-2 text-red-500">{{ formGroup.controls['id'].errors | json }}</div> <div class="flex items-center m-1">
} <label class="mr-2 text-white"> ID: </label>
</div> <input
<app-params-form type="text"
[paramsSchema]="schema()?.properties?.params" class="pl-1 border-2 border-gray-200 text-white border-solid rounded-sm"
[data]="params()" [placeholder]="'module-1'"
(updated)="paramsUpdated($event)" [formControlName]="'id'"
></app-params-form> />
<mat-icon class="text-white!" matTooltip="String">abc</mat-icon>
@if (!formGroup.controls['id'].valid) {
<!-- TODO(jwetzell): better error displaying -->
<div class="ml-2 text-red-500">{{ formGroup.controls['id'].errors | json }}</div>
}
</div> </div>
</form> <app-params-form
</div> [paramsSchema]="schema()?.properties?.params"
<div> [data]="params()"
(updated)="paramsUpdated($event)"
></app-params-form>
</div>
</form>
</div>
<div>
@if (moduleConfigErrors().length > 0) { @if (moduleConfigErrors().length > 0) {
<div class="m-2 p-2 border-2 border-red-500"> <div class="m-2 p-2 border-2 border-red-500">
<div class="flex items-center mb-2 text-red-500"> <div class="flex items-center mb-2 text-red-500">
@@ -58,5 +65,5 @@
</div> </div>
} }
</div> </div>
</div> </div>
} }
@@ -1,95 +1,95 @@
import { ComponentFixture, TestBed } from '@angular/core/testing' import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ParamsFormComponent } from './params-form'; import { ParamsFormComponent } from './params-form';
import { SchemaService } from '../../services/schema'; import { SchemaService } from '../../services/schema';
import { SomeJSONSchema } from 'ajv/dist/types/json-schema'; import { SomeJSONSchema } from 'ajv/dist/types/json-schema';
describe('ParamsForm', ()=>{ describe('ParamsForm', () => {
let component: ParamsFormComponent; let component: ParamsFormComponent;
let fixture: ComponentFixture<ParamsFormComponent>; let fixture: ComponentFixture<ParamsFormComponent>;
beforeEach(async ()=>{ beforeEach(async () => {
fixture = TestBed.createComponent(ParamsFormComponent); fixture = TestBed.createComponent(ParamsFormComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
await fixture.whenStable(); await fixture.whenStable();
}) });
it('should create', ()=>{ it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}) });
it('should create form for schema', async ()=>{ it('should create form for schema', async () => {
const paramsSchema: SomeJSONSchema = { const paramsSchema: SomeJSONSchema = {
type: "object", type: 'object',
properties: {}, properties: {},
required: [] required: [],
} };
fixture.componentRef.setInput('paramsSchema', paramsSchema) fixture.componentRef.setInput('paramsSchema', paramsSchema);
await fixture.whenStable() await fixture.whenStable();
expect(fixture.nativeElement.querySelector('form')).toBeTruthy() expect(fixture.nativeElement.querySelector('form')).toBeTruthy();
}) });
it('form should have input for string property', async ()=>{ it('form should have input for string property', async () => {
const paramsSchema: SomeJSONSchema = { const paramsSchema: SomeJSONSchema = {
type: "object", type: 'object',
properties: { properties: {
"test": { test: {
type: "string", type: 'string',
} },
}, },
required: [] required: [],
} };
fixture.componentRef.setInput('paramsSchema', paramsSchema) fixture.componentRef.setInput('paramsSchema', paramsSchema);
await fixture.whenStable() await fixture.whenStable();
const formEl = fixture.nativeElement.querySelector('form') const formEl = fixture.nativeElement.querySelector('form');
expect(formEl).toBeDefined() expect(formEl).toBeDefined();
const inputEl = formEl.querySelector('input') const inputEl = formEl.querySelector('input');
expect(inputEl).toBeDefined() expect(inputEl).toBeDefined();
}) });
it('form should have select for string enum property', async ()=>{ it('form should have select for string enum property', async () => {
const paramsSchema: SomeJSONSchema = { const paramsSchema: SomeJSONSchema = {
type: "object", type: 'object',
properties: { properties: {
"test": { test: {
type: "string", type: 'string',
enum: ['one','two','three'] enum: ['one', 'two', 'three'],
} },
}, },
required: [] required: [],
} };
fixture.componentRef.setInput('paramsSchema', paramsSchema) fixture.componentRef.setInput('paramsSchema', paramsSchema);
await fixture.whenStable() await fixture.whenStable();
const formEl = fixture.nativeElement.querySelector('form') const formEl = fixture.nativeElement.querySelector('form');
expect(formEl).toBeDefined() expect(formEl).toBeDefined();
const selectEl = formEl.querySelector('select') const selectEl = formEl.querySelector('select');
expect(selectEl).toBeDefined() expect(selectEl).toBeDefined();
}) });
it('form input should reflect data', async ()=>{ it('form input should reflect data', async () => {
const paramsSchema: SomeJSONSchema = { const paramsSchema: SomeJSONSchema = {
type: "object", type: 'object',
properties: { properties: {
"test": { test: {
type: "string", type: 'string',
} },
}, },
required: [] required: [],
} };
fixture.componentRef.setInput('paramsSchema', paramsSchema) fixture.componentRef.setInput('paramsSchema', paramsSchema);
await fixture.whenStable() await fixture.whenStable();
fixture.componentRef.setInput('data', { fixture.componentRef.setInput('data', {
test: 'hello' test: 'hello',
}) });
await fixture.whenStable() await fixture.whenStable();
const inputEl = fixture.nativeElement.querySelector('input') const inputEl = fixture.nativeElement.querySelector('input');
expect(inputEl).toBeDefined() expect(inputEl).toBeDefined();
expect(inputEl.value).toBe('hello') expect(inputEl.value).toBe('hello');
fixture.componentRef.setInput('data', { fixture.componentRef.setInput('data', {
test: 'changed' test: 'changed',
}) });
await fixture.whenStable() await fixture.whenStable();
expect(inputEl.value).toBe('changed') expect(inputEl.value).toBe('changed');
}) });
}) });
@@ -1,11 +1,5 @@
import { JsonPipe } from '@angular/common'; import { JsonPipe } from '@angular/common';
import { import { Component, effect, input, OnDestroy, output } from '@angular/core';
Component,
effect,
input,
OnDestroy,
output
} 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';
+5 -2
View File
@@ -9,12 +9,15 @@
<div class="grow ml-1 mr-3 text-white"> <div class="grow ml-1 mr-3 text-white">
{{ schema()?.title || processor()?.type }} {{ schema()?.title || processor()?.type }}
</div> </div>
<div class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer" (click)="delete.emit()"> <div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(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>
@if (hasParams()) { @if (hasParams()) {
<hr class="bg-gray-600 h-0.5 border-0"> <hr class="bg-gray-600 h-0.5 border-0" />
<div> <div>
<app-params-form <app-params-form
[paramsSchema]="schema()?.properties?.params" [paramsSchema]="schema()?.properties?.params"
+2 -2
View File
@@ -37,7 +37,7 @@ export class ProcessorComponent {
paramsUpdated(params: any) { paramsUpdated(params: any) {
this.processor.update((processor) => { this.processor.update((processor) => {
if (processor !== undefined) { if (processor !== undefined) {
if (params !== undefined){ if (params !== undefined) {
return { return {
...processor, ...processor,
params: params, params: params,
@@ -45,7 +45,7 @@ export class ProcessorComponent {
} }
return { return {
...processor, ...processor,
} };
} }
return undefined; return undefined;
}); });
@@ -12,7 +12,7 @@
<div class="grow overflow-x-hidden overflow-y-auto gap-1.5"> <div class="grow overflow-x-hidden overflow-y-auto gap-1.5">
@for (route of routes(); track $index; let i = $index) { @for (route of routes(); track $index; let i = $index) {
<div class="m-2"> <div class="m-2">
<app-route <app-route
[path]="`routes/${i}`" [path]="`routes/${i}`"
(routeChange)="routeUpdated(i, $event)" (routeChange)="routeUpdated(i, $event)"
[route]="route" [route]="route"
+2 -9
View File
@@ -10,24 +10,17 @@ import { MatTooltipModule } from '@angular/material/tooltip';
@Component({ @Component({
selector: 'app-route-list', selector: 'app-route-list',
imports: [ imports: [RouteComponent, MatMenuModule, MatIconModule, MatButtonModule, MatTooltipModule],
RouteComponent,
MatMenuModule,
MatIconModule,
MatButtonModule,
MatTooltipModule,
],
templateUrl: './route-list.html', templateUrl: './route-list.html',
styleUrl: './route-list.css', styleUrl: './route-list.css',
}) })
export class RouteListComponent { export class RouteListComponent {
routes = model<RouteConfig[]>(); routes = model<RouteConfig[]>();
moduleIds = input<string[]>(); moduleIds = input<string[]>();
public schemaService = inject(SchemaService); public schemaService = inject(SchemaService);
private snackBar = inject(MatSnackBar); private snackBar = inject(MatSnackBar);
deleteRoute(index: number) { deleteRoute(index: number) {
this.routes.update((routes) => { this.routes.update((routes) => {
if (routes) { if (routes) {
+10 -5
View File
@@ -7,14 +7,19 @@
> >
<div class="flex items-center justify-end"> <div class="flex items-center justify-end">
<div class="flex items-center"> <div class="flex items-center">
<mat-icon [style.color]="indicatorColor()" matTooltip="Route Indicator">chevron_right</mat-icon> <mat-icon [style.color]="indicatorColor()" matTooltip="Route Indicator"
>chevron_right</mat-icon
>
</div> </div>
<div class="grow text-left ml-2 text-white">Route {{ index() }}</div> <div class="grow text-left ml-2 text-white">Route {{ index() }}</div>
<div class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer" (click)="delete.emit()"> <div
class="flex items-center justify-center hover:bg-gray-700 hover:cursor-pointer"
(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>
<hr class="bg-gray-600 h-0.5 border-0"> <hr class="bg-gray-600 h-0.5 border-0" />
<div> <div>
<form [formGroup]="formGroup"> <form [formGroup]="formGroup">
<div class="m-2"> <div class="m-2">
@@ -58,7 +63,7 @@
<div class="text-md text-white text-center">Processors</div> <div class="text-md text-white text-center">Processors</div>
</div> </div>
@if (processors() !== undefined && processors()!.length > 0) { @if (processors() !== undefined && processors()!.length > 0) {
<div class="border-2 border-gray-500 m-2"> <div class="border-2 border-gray-500 m-2">
@for (processor of processors(); track $index; let i = $index) { @for (processor of processors(); track $index; let i = $index) {
<div cdkDrag> <div cdkDrag>
<app-processor <app-processor
@@ -70,7 +75,7 @@
</div> </div>
} }
</div> </div>
} }
</div> </div>
</div> </div>
<div> <div>
+12 -9
View File
@@ -91,14 +91,17 @@ export class RouteComponent {
}); });
if (this.index() !== undefined) { if (this.index() !== undefined) {
this.eventsService.getRouteEventsForIndex(this.index()!).pipe( this.eventsService
tap((routeEvent)=>{ .getRouteEventsForIndex(this.index()!)
this.indicatorColor.set(routeEvent.error ? 'red' : 'greenyellow'); .pipe(
}), tap((routeEvent) => {
debounceTime(100) this.indicatorColor.set(routeEvent.error ? 'red' : 'greenyellow');
).subscribe((routeEvent) => { }),
this.indicatorColor.set('gray') debounceTime(100),
}) )
.subscribe((routeEvent) => {
this.indicatorColor.set('gray');
});
} }
} }
@@ -115,7 +118,7 @@ export class RouteComponent {
this.route.update((route) => { this.route.update((route) => {
if (route) { if (route) {
const processors = route.processors || []; const processors = route.processors || [];
processors.push(processorTemplate); processors.push(processorTemplate);
return { return {
...route, ...route,
+4 -5
View File
@@ -1,5 +1,5 @@
export type Config = { export type Config = {
api: ApiConfig api: ApiConfig;
modules: ModuleConfig[]; modules: ModuleConfig[];
routes: RouteConfig[]; routes: RouteConfig[];
}; };
@@ -31,20 +31,19 @@ export type ProcessorConfig = {
params?: Record<string, any>; params?: Record<string, any>;
}; };
export type ModuleError = { export type ModuleError = {
index: number; index: number;
config: ModuleConfig; config: ModuleConfig;
error: string; error: string;
} };
export type RouteError = { export type RouteError = {
index: number; index: number;
config: RouteConfig; config: RouteConfig;
error: string; error: string;
} };
export type ConfigError = { export type ConfigError = {
moduleErrors?: ModuleError[]; moduleErrors?: ModuleError[];
routeErrors?: RouteError[]; routeErrors?: RouteError[];
} };
+11 -11
View File
@@ -1,19 +1,19 @@
import { Config } from "./config" import { Config } from './config';
export type RouterEvent<T, D> = { export type RouterEvent<T, D> = {
type: T, type: T;
data?: D, data?: D;
error?: string, error?: string;
} };
export type RouteEventData = { export type RouteEventData = {
index: number index: number;
} };
export type InputEventData = { export type InputEventData = {
source: string source: string;
} };
export type OutputEventData = { export type OutputEventData = {
destination: string destination: string;
} };
+5 -5
View File
@@ -34,20 +34,20 @@ export class ConfigService {
private http = inject(HttpClient); private http = inject(HttpClient);
private settingsService = inject(SettingsService); private settingsService = inject(SettingsService);
private eventsService = inject(EventsService); private eventsService = inject(EventsService);
constructor(private schemaService: SchemaService) { constructor(private schemaService: SchemaService) {
effect(() => { effect(() => {
console.log('config state changed', this.currentlyShownConfig()); console.log('config state changed', this.currentlyShownConfig());
}); });
effect(() =>{ effect(() => {
switch (this.eventsService.status()) { switch (this.eventsService.status()) {
case 'open': case 'open':
console.log('Websocket connection opened, reloading config'); console.log('Websocket connection opened, reloading config');
this.loadConfig(); this.loadConfig();
break; break;
} }
}) });
} }
loadConfig() { loadConfig() {
@@ -107,9 +107,9 @@ export class ConfigService {
setEmptyConfig() { setEmptyConfig() {
console.log('Setting empty config'); console.log('Setting empty config');
this.updateCurrentlyShownConfig({ this.updateCurrentlyShownConfig({
api: { api: {
enabled: true, enabled: true,
port: 8080 port: 8080,
}, },
modules: [], modules: [],
routes: [], routes: [],
+1 -6
View File
@@ -1,10 +1,5 @@
import { effect, inject, Injectable, signal } from '@angular/core'; import { effect, inject, Injectable, signal } from '@angular/core';
import { import { InputEventData, OutputEventData, RouteEventData, RouterEvent } from '../models/events';
InputEventData,
OutputEventData,
RouteEventData,
RouterEvent,
} from '../models/events';
import { filter, Subject } from 'rxjs'; import { filter, Subject } from 'rxjs';
import { SettingsService } from './settings'; import { SettingsService } from './settings';
@Injectable({ @Injectable({
-2
View File
@@ -286,8 +286,6 @@ export class SchemaService {
return 0; return 0;
} }
cleanArray(values: any[], itemSchema: SomeJSONSchema) { cleanArray(values: any[], itemSchema: SomeJSONSchema) {
if (Array.isArray(values)) { if (Array.isArray(values)) {
switch (itemSchema.type) { switch (itemSchema.type) {
+13 -13
View File
@@ -1,6 +1,13 @@
import { FormGroup, ValidatorFn, Validators, FormControl, AbstractControl, ValidationErrors } from "@angular/forms"; import {
import { SomeJSONSchema } from "ajv/dist/types/json-schema"; FormGroup,
import { ParamsFormInfo } from "../models/form"; 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 { export function schemaToParamsFormInfo(schema: SomeJSONSchema): ParamsFormInfo {
const paramsFormInfo: ParamsFormInfo = { const paramsFormInfo: ParamsFormInfo = {
@@ -67,10 +74,7 @@ export function schemaToParamsFormInfo(schema: SomeJSONSchema): ParamsFormInfo {
} }
//TODO(jwetzell): figure out how to disable a control but not have to deal with undefined values on disabled controls //TODO(jwetzell): figure out how to disable a control but not have to deal with undefined values on disabled controls
paramsFormInfo.formGroup.addControl( paramsFormInfo.formGroup.addControl(paramKey, new FormControl(formDefault, validators));
paramKey,
new FormControl(formDefault, validators),
);
if (paramSchema.enum) { if (paramSchema.enum) {
paramsFormInfo.paramsInfo[paramKey].options = paramSchema.enum; paramsFormInfo.paramsInfo[paramKey].options = paramSchema.enum;
@@ -100,11 +104,7 @@ export function cleanParams(paramsSchema: SomeJSONSchema, params: any): any {
const paramSchema = paramsSchema.properties[paramKey]; const paramSchema = paramsSchema.properties[paramKey];
// delete null/undefined/empty params that aren't required // delete null/undefined/empty params that aren't required
if ( if (params[paramKey] === undefined || params[paramKey] === null || params[paramKey] === '') {
params[paramKey] === undefined ||
params[paramKey] === null ||
params[paramKey] === ''
) {
if (paramSchema.required) { if (paramSchema.required) {
if (!paramSchema.includes(paramKey)) { if (!paramSchema.includes(paramKey)) {
delete params[paramKey]; delete params[paramKey];
@@ -203,4 +203,4 @@ export function parseStringToArray(value: any, schema: SomeJSONSchema): any[] |
} }
} }
return undefined; return undefined;
} }