diff --git a/config-editor/config-editor-ui/package.json b/config-editor/config-editor-ui/package.json index 337534ebf..d9e02fa95 100644 --- a/config-editor/config-editor-ui/package.json +++ b/config-editor/config-editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "rule-editor.ui", - "version": "1.3.9-dev", + "version": "1.3.10-dev", "license": "MIT", "scripts": { "ng": "ng", diff --git a/config-editor/config-editor-ui/src/app/app.module.ts b/config-editor/config-editor-ui/src/app/app.module.ts index 4545824f5..e69bb44d2 100644 --- a/config-editor/config-editor-ui/src/app/app.module.ts +++ b/config-editor/config-editor-ui/src/app/app.module.ts @@ -16,6 +16,7 @@ import { SubmitDialogComponent, LandingPageComponent, ImporterDialogComponent, + ApplicationDialogComponent, SearchComponent, } from '@app/components'; import { ConfigTileComponent } from '@app/components/tile/config-tile.component'; @@ -109,6 +110,7 @@ const DEV_PROVIDERS = [...PROD_PROVIDERS]; DeployDialogComponent, SubmitDialogComponent, ImporterDialogComponent, + ApplicationDialogComponent, LandingPageComponent, SearchComponent, ConfigTileComponent, diff --git a/config-editor/config-editor-ui/src/app/components/admin/admin.component.html b/config-editor/config-editor-ui/src/app/components/admin/admin.component.html index 5ac79ea0c..de5166ee5 100644 --- a/config-editor/config-editor-ui/src/app/components/admin/admin.component.html +++ b/config-editor/config-editor-ui/src/app/components/admin/admin.component.html @@ -1,6 +1,14 @@
{{serviceName | titlecase}} Admin Config
+

diff --git a/config-editor/config-editor-ui/src/app/components/admin/admin.component.ts b/config-editor/config-editor-ui/src/app/components/admin/admin.component.ts index bfeb31e00..68795728c 100644 --- a/config-editor/config-editor-ui/src/app/components/admin/admin.component.ts +++ b/config-editor/config-editor-ui/src/app/components/admin/admin.component.ts @@ -3,7 +3,7 @@ import { FormGroup } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { EditorService } from '@services/editor.service'; import { ConfigData, PullRequestInfo } from '@app/model'; -import { Type, AdminConfig } from '@app/model/config-model'; +import { Type, AdminConfig, Application } from '@app/model/config-model'; import { PopupService } from '@app/services/popup.service'; import { FormlyFieldConfig, FormlyFormOptions } from '@ngx-formly/core'; import { cloneDeep } from 'lodash'; @@ -13,6 +13,7 @@ import { Router } from '@angular/router'; import { SubmitDialogComponent } from '../submit-dialog/submit-dialog.component'; import { BlockUI, NgBlockUI } from 'ng-block-ui'; import { AppConfigService } from '@app/services/app-config.service'; +import { ApplicationDialogComponent } from '..'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -32,6 +33,7 @@ export class AdminComponent implements OnInit, OnDestroy { config: AdminConfig; serviceName: string; adminPullRequestPending$: Observable; + applications$: Observable; private markHistoryChange = false; private readonly PR_OPEN_MESSAGE = 'A pull request is already open'; @@ -61,6 +63,7 @@ export class AdminComponent implements OnInit, OnDestroy { } this.markHistoryChange = false; }); + this.applications$ = this.editorService.configLoader.getApplications(); } ngOnDestroy() { @@ -107,6 +110,10 @@ export class AdminComponent implements OnInit, OnDestroy { this.form.updateValueAndValidity(); } + openApplicationDialog() { + this.dialog.open(ApplicationDialogComponent, { data: this.applications$}); +} + private updateAndWrapConfig(config: AdminConfig) { //NOTE: in the form we are using wrapping config to handle optionals, unions const configData = this.editorService.adminSchema.wrapConfig(config.configData); diff --git a/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.html b/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.html new file mode 100644 index 000000000..802c51adf --- /dev/null +++ b/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.html @@ -0,0 +1,49 @@ +

Application Manager

+ + Filter + + +
+ + + + + + + + + + + + + + + +
+ {{column.header}} + + {{column.cell(row)}} + + + + +
+
+
+ +
+ + +
+ + +
+
+ +
+
\ No newline at end of file diff --git a/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.scss b/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.scss new file mode 100644 index 000000000..2ea66cdcf --- /dev/null +++ b/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.scss @@ -0,0 +1,34 @@ +.container { + font-size: 70%; + display: flex; + justify-content: space-between; + align-items: stretch; + padding: 10px 10px; + padding-right: 30px; + max-width: 70vw; + max-height: 70vh; + overflow-y: auto; +} + +.button-layout { + margin-right: 0px; + margin-left: auto; +} + +.mat-header-cell, .mat-cell { + padding: 10px; +} + +.scrollbar { + --scrollbar-thumb-color: #868686; + --scrollbar-thumb-hover-color: #a1a1a1; +} + +.attributes { + max-height: 60vh; + overflow-y: auto; +} + +.filter { + width: 100%; +} \ No newline at end of file diff --git a/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.ts b/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.ts new file mode 100644 index 000000000..f3a563311 --- /dev/null +++ b/config-editor/config-editor-ui/src/app/components/application-dialog/application-dialog.component.ts @@ -0,0 +1,68 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, TemplateRef } from "@angular/core"; +import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { MatTableDataSource } from "@angular/material/table"; +import { Application, applicationManagerColumns, displayedApplicationManagerColumns } from "@app/model/config-model"; +import { EditorService } from "@app/services/editor.service"; +import { Observable } from "rxjs"; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 're-application-dialog', + styleUrls: ['application-dialog.component.scss'], + templateUrl: 'application-dialog.component.html', +}) +export class ApplicationDialogComponent { + dialogrefAttributes: MatDialogRef; + applications$: Observable; + dataSource: MatTableDataSource; + columns = applicationManagerColumns; + displayedColumns = displayedApplicationManagerColumns; + + constructor( + private dialogref: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: Observable, + private service: EditorService, + private dialog: MatDialog, + private cd: ChangeDetectorRef + ) { + this.applications$ = data; + this.applications$.subscribe(a => { + this.createTable(a); + }) + } + + onClickClose() { + this.dialogref.close(); + } + + onRestartApplication(applicationName: string) { + this.applications$ = this.service.configLoader.restartApplication(applicationName); + this.applications$.subscribe(a => { + this.createTable(a); + }) + } + + onViewAttributes(attributes: string, templateRef: TemplateRef) { + this.dialogrefAttributes = this.dialog.open( + templateRef, + { + data: JSON.parse(atob(attributes)), + }); + } + + onClickCloseAttributes() { + this.dialogrefAttributes.close(); + } + + applyFilter(event: Event) { + const filterValue = (event.target as HTMLInputElement).value; + this.dataSource.filter = filterValue.trim().toLowerCase(); + } + + private createTable(a: Application[]) { + const filter = this.dataSource?.filter; + this.dataSource = new MatTableDataSource(a); + this.dataSource.filter = filter; + this.cd.markForCheck(); + } +} \ No newline at end of file diff --git a/config-editor/config-editor-ui/src/app/components/index.ts b/config-editor/config-editor-ui/src/app/components/index.ts index 94ba2ff74..bb7dfa5c1 100644 --- a/config-editor/config-editor-ui/src/app/components/index.ts +++ b/config-editor/config-editor-ui/src/app/components/index.ts @@ -7,4 +7,5 @@ export { DeployDialogComponent } from './deploy-dialog/deploy-dialog.component'; export { ErrorDialogComponent } from './error-dialog/error-dialog.component'; export { LandingPageComponent } from './landing-page/landing-page.component'; export { SearchComponent } from './search/search.component'; -export { ImporterDialogComponent } from './importer-dialog/importer-dialog.component'; \ No newline at end of file +export { ImporterDialogComponent } from './importer-dialog/importer-dialog.component'; +export { ApplicationDialogComponent } from './application-dialog/application-dialog.component'; \ No newline at end of file diff --git a/config-editor/config-editor-ui/src/app/components/json-tree/json-tree.component.ts b/config-editor/config-editor-ui/src/app/components/json-tree/json-tree.component.ts index 4582aeb81..efae8ab2a 100644 --- a/config-editor/config-editor-ui/src/app/components/json-tree/json-tree.component.ts +++ b/config-editor/config-editor-ui/src/app/components/json-tree/json-tree.component.ts @@ -53,8 +53,8 @@ export class JsonTreeComponent implements OnChanges { private parseKeyValue(key: any, value: any): Segment { const segment: Segment = { - key: key, - value: value, + key, + value, type: undefined, description: '' + value, expanded: this.expanded, @@ -91,12 +91,12 @@ export class JsonTreeComponent implements OnChanges { segment.description = 'null'; } else if (Array.isArray(segment.value)) { segment.type = 'array'; - segment.description = 'Array[' + segment.value.length + '] ' + JSON.stringify(segment.value); + segment.description = 'Array[' + segment.value.length + '] '; } else if (segment.value instanceof Date) { segment.type = 'date'; } else { segment.type = 'object'; - segment.description = 'Object ' + JSON.stringify(segment.value); + segment.description = 'Object '; } break; } @@ -124,8 +124,8 @@ export class JsonTreeComponent implements OnChanges { if (isIndex) { return part1 + part2; - } else { + } return part1 + '.' + part2; - } + } } diff --git a/config-editor/config-editor-ui/src/app/components/nav-bar/nav-bar.component.html b/config-editor/config-editor-ui/src/app/components/nav-bar/nav-bar.component.html index 6b933a5f0..ff863a330 100644 --- a/config-editor/config-editor-ui/src/app/components/nav-bar/nav-bar.component.html +++ b/config-editor/config-editor-ui/src/app/components/nav-bar/nav-bar.component.html @@ -19,24 +19,26 @@

{{serviceName$ | async | titlecase}}

arrow_drop_down
- -

Admin

-
- +
+ +

Admin

+
+ +
; serviceName: string; @@ -54,19 +53,19 @@ export class NavBarComponent implements OnInit { }); } - public showAboutApp() { + showAboutApp() { this.dialog.open(BuildInfoDialogComponent, { data: this.config.buildInfo }).afterClosed().subscribe(); } - public onToggleAdmin() { - let path = this.isAdminChecked ? this.config.adminPath : ""; + onToggleAdmin() { + const path = this.isAdminChecked ? this.config.adminPath : ""; this.router.navigate([this.serviceName + path]); } - public getPath(service: string): string { + getPath(service: string): string { let path = service; const roles = this.appService.getUserServiceRoles(service); - let hasMultipleUserRoles = roles.length > 1; + const hasMultipleUserRoles = roles.length > 1; if ((hasMultipleUserRoles && this.isAdminChecked) || (!hasMultipleUserRoles && roles.includes(UserRole.SERVICE_ADMIN))) { path += this.config.adminPath; } diff --git a/config-editor/config-editor-ui/src/app/model/config-model.ts b/config-editor/config-editor-ui/src/app/model/config-model.ts index a5f10163b..c9bd75915 100644 --- a/config-editor/config-editor-ui/src/app/model/config-model.ts +++ b/config-editor/config-editor-ui/src/app/model/config-model.ts @@ -192,4 +192,36 @@ export interface ConfigToImport { export interface ImportedConfig { imported_configuration: any; -} \ No newline at end of file +} + +export interface applications { + topologies: Application[]; +} + +export interface Application { + topology_name: string, + topology_id: string, + attributes: string[], + image: string, + service_name: string +} + +export const applicationManagerColumns = [ + { + columnDef: 'name', + header: 'Name', + cell: (t: Application) => `${t.topology_name}`, + }, + { + columnDef: 'id', + header: 'ID', + cell: (t: Application) => `${t.topology_id}`, + }, + { + columnDef: 'image', + header: 'Image', + cell: (t: Application) => `${t.image}`, + }, +]; + +export const displayedApplicationManagerColumns = ["name", "id", "image", "attributes", "restart"]; \ No newline at end of file diff --git a/config-editor/config-editor-ui/src/app/services/config-loader.service.ts b/config-editor/config-editor-ui/src/app/services/config-loader.service.ts index a1b3c6265..5dd367d6b 100644 --- a/config-editor/config-editor-ui/src/app/services/config-loader.service.ts +++ b/config-editor/config-editor-ui/src/app/services/config-loader.service.ts @@ -21,6 +21,8 @@ import { Importers, ImportedConfig, ConfigToImport, + applications, + Application, } from '@model/config-model'; import { TestCase, TestCaseMap, TestCaseResult, TestCaseWrapper } from '@model/test-case'; import { ADMIN_VERSION_FIELD_NAME, UiMetadata } from '@model/ui-metadata-map'; @@ -412,6 +414,19 @@ export class ConfigLoaderService { .pipe(map(result => result)); } + getApplications(): Observable { + return this.http.get( + `${this.config.serviceRoot}api/v1/${this.serviceName}/topologies` + ).pipe(map(result => result.topologies)); + } + + restartApplication(application: string): Observable { + return this.http.post( + `${this.config.serviceRoot}api/v1/${this.serviceName}/topologies/${application}/restart`, + null + ).pipe(map(result => result.topologies.filter(t => t.service_name === this.serviceName))); + } + private testCaseFilesToMap(files: any[]): TestCaseMap { const testCaseMap: TestCaseMap = {}; if (files && files.length > 0) {