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