diff --git a/frontend/src/app/history-query/history-query-detail/history-query-detail.component.html b/frontend/src/app/history-query/history-query-detail/history-query-detail.component.html index 957c3f7e66..4662e2a956 100644 --- a/frontend/src/app/history-query/history-query-detail/history-query-detail.component.html +++ b/frontend/src/app/history-query/history-query-detail/history-query-detail.component.html @@ -185,6 +185,17 @@

+ + + +
+
+ {{ 'logs.title' | translate }} +
+
+
+
+ diff --git a/frontend/src/app/history-query/history-query-detail/history-query-detail.component.spec.ts b/frontend/src/app/history-query/history-query-detail/history-query-detail.component.spec.ts index 1935af0f83..828933e409 100644 --- a/frontend/src/app/history-query/history-query-detail/history-query-detail.component.spec.ts +++ b/frontend/src/app/history-query/history-query-detail/history-query-detail.component.spec.ts @@ -37,6 +37,10 @@ class HistoryQueryDisplayComponentTester extends ComponentTester { @@ -239,6 +243,11 @@ describe('HistoryQueryDisplayComponent', () => { expect(item.elements('td')[2]).toContainText('sql'); }); + it('should display logs', () => { + tester.detectChanges(); + expect(tester.historyQueryLogs.length).toBe(1); + }); + it('should test north connection', () => { tester.componentInstance.northManifest = northManifest; tester.componentInstance.historyQuery = historyQuery; diff --git a/frontend/src/app/history-query/history-query-detail/history-query-detail.component.ts b/frontend/src/app/history-query/history-query-detail/history-query-detail.component.ts index b43cbd4df2..84960baca5 100644 --- a/frontend/src/app/history-query/history-query-detail/history-query-detail.component.ts +++ b/frontend/src/app/history-query/history-query-detail/history-query-detail.component.ts @@ -34,6 +34,7 @@ import { EngineService } from '../../services/engine.service'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { ModalService } from '../../shared/modal.service'; import { TestConnectionResultModalComponent } from '../../shared/test-connection-result-modal/test-connection-result-modal.component'; +import { LogsComponent } from '../../logs/logs.component'; @Component({ selector: 'oib-history-query-detail', @@ -53,7 +54,8 @@ import { TestConnectionResultModalComponent } from '../../shared/test-connection HistoryMetricsComponent, SouthMetricsComponent, AsyncPipe, - ClipboardModule + ClipboardModule, + LogsComponent ], templateUrl: './history-query-detail.component.html', styleUrl: './history-query-detail.component.scss', @@ -76,6 +78,7 @@ export class HistoryQueryDetailComponent implements OnInit, OnDestroy { historyStream: EventSource | null = null; state = new ObservableState(); oibusInfo: OIBusInfo | null = null; + historyQueryId: string | null = null; constructor( private historyQueryService: HistoryQueryService, @@ -99,9 +102,10 @@ export class HistoryQueryDetailComponent implements OnInit, OnDestroy { this.route.paramMap .pipe( switchMap(params => { - const paramHistoryQueryId = params.get('historyQueryId') || ''; - if (paramHistoryQueryId) { - return this.historyQueryService.get(paramHistoryQueryId); + this.historyQueryId = params.get('historyQueryId') || ''; + + if (this.historyQueryId) { + return this.historyQueryService.get(this.historyQueryId); } return of(null); }), diff --git a/frontend/src/app/logs/logs.component.html b/frontend/src/app/logs/logs.component.html index cdd9411a5e..5da2b69ce9 100644 --- a/frontend/src/app/logs/logs.component.html +++ b/frontend/src/app/logs/logs.component.html @@ -1,13 +1,15 @@ -
+
-
-
-
- + @if (!this.scopeId && !this.scopeType) { +
+
+
+ +
+

{{ 'logs.title' | translate }}

-

{{ 'logs.title' | translate }}

-
+ }
@@ -21,46 +23,57 @@

{{ 'logs.title' | translate }}

- -
- - @for (level of levels; track level) { - - } - -
- - -
- - @for (scopeType of scopeTypes; track scopeType) { - - } - -
+ @if (!this.scopeId && !this.scopeType) { + +
+ + @for (level of levels; track level) { + + } + +
+ +
+ + @for (scopeType of scopeTypes; track scopeType) { + + } + +
+ }
- -
- -
- @for (scope of selectedScopes; track scope.scopeId) { - {{ scope.scopeName }} - } + @if (!this.scopeId && !this.scopeType) { + +
+ +
+ @for (scope of selectedScopes; track scope.scopeId) { + {{ scope.scopeName }} + } +
-
- + } @else { + +
+ + @for (level of levels; track level) { + + } + +
+ } -
+
@@ -89,25 +102,28 @@

{{ 'logs.title' | translate }}

@if (logs.totalElements !== 0) {
- + - - - + @if (!this.scopeId && !this.scopeType) { + + + } + @for (log of logs.content; track log) { - - + @if (!this.scopeId && !this.scopeType) { + + + } } diff --git a/frontend/src/app/logs/logs.component.ts b/frontend/src/app/logs/logs.component.ts index 069eaa81de..7f42a91e17 100644 --- a/frontend/src/app/logs/logs.component.ts +++ b/frontend/src/app/logs/logs.component.ts @@ -1,4 +1,4 @@ -import { Component, inject, OnDestroy, OnInit } from '@angular/core'; +import { Component, inject, OnDestroy, OnInit, Input } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { ActivatedRoute, Router } from '@angular/router'; import { PageLoader } from '../shared/page-loader.service'; @@ -37,6 +37,7 @@ import { TYPEAHEAD_DEBOUNCE_TIME } from '../shared/typeahead'; import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; import { PillComponent } from '../shared/pill/pill.component'; import { LegendComponent } from '../shared/legend/legend.component'; +import { NgClass } from '@angular/common'; @Component({ selector: 'oib-logs', @@ -53,13 +54,17 @@ import { LegendComponent } from '../shared/legend/legend.component'; ScopeTypesEnumPipe, NgbTypeahead, PillComponent, - LegendComponent + LegendComponent, + NgClass ], templateUrl: './logs.component.html', styleUrl: './logs.component.scss', providers: [PageLoader] }) export class LogsComponent implements OnInit, OnDestroy { + @Input() scopeId: string | null = null; + @Input() scopeType: ScopeType | null = null; + readonly searchForm = inject(NonNullableFormBuilder).group( { messageContent: null as string | null, @@ -121,6 +126,10 @@ export class LogsComponent implements OnInit, OnDestroy { } ngOnInit(): void { + if (this.scopeId !== null && this.scopeType !== null) { + this.searchForm.controls.scopeTypes.disable(); + this.searchForm.controls.scopeIds.disable(); + } const queryScopeIds = this.route.snapshot.queryParamMap.getAll('scopeIds'); if (queryScopeIds.length > 0) { combineLatest(queryScopeIds.map(scopeId => this.logService.getScopeById(scopeId))).subscribe(selectedScopes => { @@ -155,8 +164,15 @@ export class LogsComponent implements OnInit, OnDestroy { const now = DateTime.now().endOf('minute'); const queryParamMap = route.snapshot.queryParamMap; const messageContent = queryParamMap.get('messageContent'); - const scopeTypes = queryParamMap.getAll('scopeTypes'); - const scopeIds = queryParamMap.getAll('scopeIds'); + let scopeTypes = null; + let scopeIds = null; + if (this.scopeId !== null && this.scopeType !== null) { + scopeTypes = [this.scopeType]; + scopeIds = [this.scopeId]; + } else { + scopeTypes = queryParamMap.getAll('scopeTypes'); + scopeIds = queryParamMap.getAll('scopeIds'); + } const start = queryParamMap.get('start') ?? now.minus({ days: 1 }).toISO(); const end = queryParamMap.get('end'); const levels = queryParamMap.getAll('levels'); @@ -174,11 +190,10 @@ export class LogsComponent implements OnInit, OnDestroy { end: formValue.end!, messageContent: formValue.messageContent!, levels: formValue.levels!, - scopeTypes: formValue.scopeTypes!, - scopeIds: this.selectedScopes!.map(scope => scope.scopeId), + scopeTypes: this.scopeType ? [this.scopeType] : formValue.scopeTypes!, + scopeIds: this.scopeId ? [this.scopeId] : this.selectedScopes!.map(scope => scope.scopeId), page: 0 }; - this.router.navigate(['.'], { queryParams: criteria, relativeTo: this.route }); } diff --git a/frontend/src/app/north/north-detail/north-detail.component.html b/frontend/src/app/north/north-detail/north-detail.component.html index cde6d2599f..1e664150bc 100644 --- a/frontend/src/app/north/north-detail/north-detail.component.html +++ b/frontend/src/app/north/north-detail/north-detail.component.html @@ -105,6 +105,17 @@

+ + + +
+
+ {{ 'logs.title' | translate }} +
+
+
+
+ diff --git a/frontend/src/app/north/north-detail/north-detail.component.spec.ts b/frontend/src/app/north/north-detail/north-detail.component.spec.ts index 26a85cad59..fb2a43b708 100644 --- a/frontend/src/app/north/north-detail/north-detail.component.spec.ts +++ b/frontend/src/app/north/north-detail/north-detail.component.spec.ts @@ -29,6 +29,10 @@ class NorthDetailComponentTester extends ComponentTester { get northSettings() { return this.elements('tbody.north-settings tr'); } + + get northLogs() { + return this.elements('#logs-title'); + } } describe('NorthDetailComponent', () => { @@ -150,6 +154,11 @@ describe('NorthDetailComponent', () => { expect(settings[1]).toContainText('url'); }); + it('should display logs', () => { + tester.detectChanges(); + expect(tester.northLogs.length).toBe(1); + }); + it('should stop north', () => { tester.detectChanges(); tester.toggleButton.click(); diff --git a/frontend/src/app/north/north-detail/north-detail.component.ts b/frontend/src/app/north/north-detail/north-detail.component.ts index 71b0ea2db6..60d9fd9c70 100644 --- a/frontend/src/app/north/north-detail/north-detail.component.ts +++ b/frontend/src/app/north/north-detail/north-detail.component.ts @@ -23,6 +23,7 @@ import { ModalService } from '../../shared/modal.service'; import { BooleanEnumPipe } from '../../shared/boolean-enum.pipe'; import { PipeProviderService } from '../../shared/form/pipe-provider.service'; import { EngineService } from '../../services/engine.service'; +import { LogsComponent } from '../../logs/logs.component'; @Component({ selector: 'oib-north-detail', @@ -38,7 +39,8 @@ import { EngineService } from '../../services/engine.service'; BoxTitleDirective, DurationPipe, EnabledEnumPipe, - ClipboardModule + ClipboardModule, + LogsComponent ], templateUrl: './north-detail.component.html', styleUrl: './north-detail.component.scss', @@ -52,6 +54,7 @@ export class NorthDetailComponent implements OnInit, OnDestroy { connectorStream: EventSource | null = null; connectorMetrics: NorthConnectorMetrics | null = null; oibusInfo: OIBusInfo | null = null; + northId: string | null = null; constructor( private windowService: WindowService, @@ -74,10 +77,10 @@ export class NorthDetailComponent implements OnInit, OnDestroy { this.route.paramMap .pipe( switchMap(params => { - const paramNorthId = params.get('northId'); + this.northId = params.get('northId'); - if (paramNorthId) { - return this.northConnectorService.get(paramNorthId); + if (this.northId) { + return this.northConnectorService.get(this.northId); } return of(null); }), diff --git a/frontend/src/app/south/south-detail/south-detail.component.html b/frontend/src/app/south/south-detail/south-detail.component.html index 7fd7c7232a..b2f5bd1c3c 100644 --- a/frontend/src/app/south/south-detail/south-detail.component.html +++ b/frontend/src/app/south/south-detail/south-detail.component.html @@ -72,6 +72,17 @@

+ + + +
+
+ {{ 'logs.title' | translate }} +
+
+
+
+ diff --git a/frontend/src/app/south/south-detail/south-detail.component.spec.ts b/frontend/src/app/south/south-detail/south-detail.component.spec.ts index 50285e21c9..ddef07aec6 100644 --- a/frontend/src/app/south/south-detail/south-detail.component.spec.ts +++ b/frontend/src/app/south/south-detail/south-detail.component.spec.ts @@ -34,6 +34,10 @@ class SouthDisplayComponentTester extends ComponentTester get southItems() { return this.elements('tbody tr.south-item'); } + + get southLogs() { + return this.elements('#logs-title'); + } } describe('SouthDetailComponent', () => { @@ -178,6 +182,11 @@ describe('SouthDetailComponent', () => { expect(item.elements('td')[3]).toContainText('sql'); }); + it('should display logs', () => { + tester.detectChanges(); + expect(tester.southLogs.length).toBe(1); + }); + it('should stop south', () => { tester.detectChanges(); tester.toggleButton.click(); diff --git a/frontend/src/app/south/south-detail/south-detail.component.ts b/frontend/src/app/south/south-detail/south-detail.component.ts index 8b1cd7fda7..72d8ff9ad5 100644 --- a/frontend/src/app/south/south-detail/south-detail.component.ts +++ b/frontend/src/app/south/south-detail/south-detail.component.ts @@ -22,6 +22,7 @@ import { ModalService } from '../../shared/modal.service'; import { TestConnectionResultModalComponent } from '../../shared/test-connection-result-modal/test-connection-result-modal.component'; import { EngineService } from '../../services/engine.service'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { LogsComponent } from '../../logs/logs.component'; @Component({ selector: 'oib-south-detail', @@ -37,7 +38,8 @@ import { ClipboardModule } from '@angular/cdk/clipboard'; BoxTitleDirective, EnabledEnumPipe, SouthItemsComponent, - ClipboardModule + ClipboardModule, + LogsComponent ], templateUrl: './south-detail.component.html', styleUrl: './south-detail.component.scss', @@ -51,6 +53,7 @@ export class SouthDetailComponent implements OnInit, OnDestroy { connectorMetrics: SouthConnectorMetrics | null = null; connectorStream: EventSource | null = null; oibusInfo: OIBusInfo | null = null; + southId: string | null = null; constructor( private windowService: WindowService, @@ -73,10 +76,10 @@ export class SouthDetailComponent implements OnInit, OnDestroy { this.route.paramMap .pipe( switchMap(params => { - const paramSouthId = params.get('southId'); + this.southId = params.get('southId'); - if (paramSouthId) { - return this.southConnectorService.get(paramSouthId); + if (this.southId) { + return this.southConnectorService.get(this.southId); } return of(null); }),

- {{ log.timestamp | datetime: 'mediumWithSeconds' }}{{ log.scopeType | scopeTypesEnum }}{{ log.scopeName }}{{ log.scopeType | scopeTypesEnum }}{{ log.scopeName }}{{ log.message }}