You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2025/08/05 11:21:17 UTC
(streampipes) 02/02: Improve widget loading in dashboard
This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch load-dashboard-in-single-request
in repository https://gitbox.apache.org/repos/asf/streampipes.git
commit b920d0b989be89c84fe4c44ee69f957e0d27b3b0
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Tue Aug 5 13:20:54 2025 +0200
Improve widget loading in dashboard
---
.../impl/dashboard/DataLakeDashboardResource.java | 2 +-
.../src/lib/apis/dashboard.service.ts | 16 ++-
.../src/lib/model/dashboard/dashboard.model.ts | 13 ++-
.../chart-view/abstract-chart-view.directive.ts | 47 +++-----
.../grid-view/dashboard-grid-view.component.ts | 16 +--
.../slide-view/dashboard-slide-view.component.ts | 25 ++---
.../panel/dashboard-panel.component.html | 14 ++-
.../components/panel/dashboard-panel.component.ts | 120 +++++++++++++--------
.../data-explorer-chart-container.component.ts | 4 +-
.../base/base-data-explorer-widget.directive.ts | 7 +-
.../charts/image/image-widget.component.ts | 8 +-
.../models/dataview-dashboard.model.ts | 4 +-
12 files changed, 146 insertions(+), 130 deletions(-)
diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
index 38f6f72177..f97b7083a1 100644
--- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
+++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/dashboard/DataLakeDashboardResource.java
@@ -57,7 +57,7 @@ public class DataLakeDashboardResource extends AbstractAuthGuardedRestResource {
return getResourceManager().find(dashboardId);
}
- @GetMapping(path = "/{dashboardId}/full", produces = MediaType.APPLICATION_JSON_VALUE)
+ @GetMapping(path = "/{dashboardId}/composite", produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("this.hasReadAuthority() and hasPermission(#dashboardId, 'READ')")
public CompositeDashboardModel getCompositeDashboardModel(@PathVariable("dashboardId") String dashboardId) {
return getResourceManager().getCompositeDashboard(dashboardId);
diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
index 5e7753e257..4a40b03218 100644
--- a/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/apis/dashboard.service.ts
@@ -19,9 +19,11 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
import { SharedDatalakeRestService } from './shared-dashboard.service';
-import { Dashboard } from '../model/dashboard/dashboard.model';
+import {
+ CompositeDashboard,
+ Dashboard,
+} from '../model/dashboard/dashboard.model';
@Injectable({
providedIn: 'root',
@@ -37,9 +39,13 @@ export class DashboardService {
}
getDashboard(dashboardId: string): Observable<Dashboard> {
- return this.http
- .get(`${this.dashboardUrl}/${dashboardId}`)
- .pipe(map(data => data as Dashboard));
+ return this.http.get<Dashboard>(`${this.dashboardUrl}/${dashboardId}`);
+ }
+
+ getCompositeDashboard(dashboardId: string): Observable<CompositeDashboard> {
+ return this.http.get<CompositeDashboard>(
+ `${this.dashboardUrl}/${dashboardId}/composite`,
+ );
}
updateDashboard(dashboard: Dashboard): Observable<Dashboard> {
diff --git a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
index 134490d0ce..927d1a1946 100644
--- a/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/model/dashboard/dashboard.model.ts
@@ -18,7 +18,12 @@
import { GridsterConfig, GridsterItem } from 'angular-gridster2';
import { TimeSettings } from '../datalake/DateRange';
-import { ResourceMetadata } from '../gen/streampipes-model';
+import {
+ DashboardModel,
+ DataExplorerWidgetModel,
+ DataLakeMeasure,
+ ResourceMetadata,
+} from '../gen/streampipes-model';
// tslint:disable-next-line:no-empty-interface
export interface DashboardConfig extends GridsterConfig {}
@@ -49,3 +54,9 @@ export interface Dashboard {
metadata: ResourceMetadata;
rev?: string;
}
+
+export interface CompositeDashboard {
+ dashboard: Dashboard;
+ dataLakeMeasures: DataLakeMeasure[];
+ widgets: DataExplorerWidgetModel[];
+}
diff --git a/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts b/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
index f75ab2f283..bf3adc6ad4 100644
--- a/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
+++ b/ui/src/app/dashboard/components/chart-view/abstract-chart-view.directive.ts
@@ -16,7 +16,7 @@
*
*/
-import { Directive, EventEmitter, Input, Output } from '@angular/core';
+import { Directive, EventEmitter, inject, Input, Output } from '@angular/core';
import {
ChartService,
Dashboard,
@@ -25,13 +25,19 @@ import {
TimeSettings,
} from '@streampipes/platform-services';
import { ResizeService } from '../../../data-explorer-shared/services/resize.service';
-import { of, zip } from 'rxjs';
import { DataExplorerChartRegistry } from '../../../data-explorer-shared/registry/data-explorer-chart-registry';
-import { catchError } from 'rxjs/operators';
@Directive()
export abstract class AbstractChartViewDirective {
- _dashboard: Dashboard;
+ protected resizeService = inject(ResizeService);
+ protected dataViewDataExplorerService = inject(ChartService);
+ protected widgetRegistryService = inject(DataExplorerChartRegistry);
+
+ @Input()
+ dashboard: Dashboard;
+
+ @Input()
+ widgets: DataExplorerWidgetModel[] = [];
@Input()
editMode: boolean;
@@ -61,39 +67,20 @@ export abstract class AbstractChartViewDirective {
@Output() startEditModeEmitter: EventEmitter<DataExplorerWidgetModel> =
new EventEmitter<DataExplorerWidgetModel>();
- constructor(
- protected resizeService: ResizeService,
- protected dataViewDataExplorerService: ChartService,
- protected widgetRegistryService: DataExplorerChartRegistry,
- ) {}
-
startEditMode(value: DataExplorerWidgetModel) {
this.startEditModeEmitter.emit(value);
this.currentlyConfiguredWidgetId = value.elementId;
}
- @Input() set dashboard(dashboard: Dashboard) {
- this._dashboard = dashboard;
- this.loadWidgetConfigs();
- }
-
- get dashboard() {
- return this._dashboard;
- }
-
loadWidgetConfigs() {
- const observables = this.dashboard.widgets.map(w =>
- this.dataViewDataExplorerService
- .getChart(w.id)
- .pipe(catchError(() => of(undefined))),
- );
- zip(...observables).subscribe(results => {
- results.forEach(r => {
- this.processWidget(r);
- this.onWidgetsAvailable();
- this.widgetsAvailable = true;
- });
+ this.dashboard.widgets.forEach(widgetConfig => {
+ const availableWidget = this.widgets.find(
+ w => w.elementId === widgetConfig.id,
+ );
+ this.processWidget(availableWidget);
});
+ this.onWidgetsAvailable();
+ this.widgetsAvailable = true;
}
loadWidgetConfig(widgetId: string, setCurrentlyConfigured?: boolean) {
diff --git a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts b/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
index 43f8a6a37c..481454a769 100644
--- a/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
+++ b/ui/src/app/dashboard/components/chart-view/grid-view/dashboard-grid-view.component.ts
@@ -27,10 +27,7 @@ import {
import { GridsterItemComponent, GridType } from 'angular-gridster2';
import { GridsterInfo } from '../../../../data-explorer-shared/models/gridster-info.model';
import { IDataViewDashboardConfig } from '../../../../data-explorer-shared/models/dataview-dashboard.model';
-import { ResizeService } from '../../../../data-explorer-shared/services/resize.service';
-import { ChartService } from '@streampipes/platform-services';
import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
-import { DataExplorerChartRegistry } from '../../../../data-explorer-shared/registry/data-explorer-chart-registry';
@Component({
selector: 'sp-dashboard-grid-view',
@@ -48,19 +45,8 @@ export class DashboardGridViewComponent
@ViewChildren(GridsterItemComponent)
gridsterItemComponents: QueryList<GridsterItemComponent>;
- constructor(
- protected resizeService: ResizeService,
- protected dataViewDataExplorerService: ChartService,
- protected widgetRegistryService: DataExplorerChartRegistry,
- ) {
- super(
- resizeService,
- dataViewDataExplorerService,
- widgetRegistryService,
- );
- }
-
ngOnInit(): void {
+ this.loadWidgetConfigs();
this.options = {
disablePushOnDrag: true,
draggable: { enabled: this.editMode },
diff --git a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts b/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
index d5169600c6..7d6dd7b283 100644
--- a/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
+++ b/ui/src/app/dashboard/components/chart-view/slide-view/dashboard-slide-view.component.ts
@@ -16,16 +16,19 @@
*
*/
-import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
+import {
+ AfterViewInit,
+ Component,
+ ElementRef,
+ OnInit,
+ ViewChild,
+} from '@angular/core';
import { AbstractChartViewDirective } from '../abstract-chart-view.directive';
-import { ResizeService } from '../../../../data-explorer-shared/services/resize.service';
import {
- ChartService,
DashboardItem,
DataExplorerWidgetModel,
DataLakeMeasure,
} from '@streampipes/platform-services';
-import { DataExplorerChartRegistry } from '../../../../data-explorer-shared/registry/data-explorer-chart-registry';
@Component({
selector: 'sp-dashboard-slide-view',
@@ -35,7 +38,7 @@ import { DataExplorerChartRegistry } from '../../../../data-explorer-shared/regi
})
export class DashboardSlideViewComponent
extends AbstractChartViewDirective
- implements AfterViewInit
+ implements OnInit, AfterViewInit
{
selectedWidgetIndex = 0;
@@ -49,16 +52,8 @@ export class DashboardSlideViewComponent
@ViewChild('slideViewOuter') slideViewOuter: ElementRef;
- constructor(
- protected resizeService: ResizeService,
- protected dataViewDataExplorerService: ChartService,
- protected widgetRegistryService: DataExplorerChartRegistry,
- ) {
- super(
- resizeService,
- dataViewDataExplorerService,
- widgetRegistryService,
- );
+ ngOnInit() {
+ this.loadWidgetConfigs();
}
selectWidget(index: number, widgetId: string): void {
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
index ad1f0e154e..d2627f69c7 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
@@ -50,11 +50,13 @@
class="designer-panel"
>
<div fxLayout="column" fxFlex="100">
- <sp-dashboard-chart-selection-panel
- (addChartEmitter)="addChartToDashboard($event)"
- fxFlex="100"
- >
- </sp-dashboard-chart-selection-panel>
+ @if (editMode) {
+ <sp-dashboard-chart-selection-panel
+ (addChartEmitter)="addChartToDashboard($event)"
+ fxFlex="100"
+ >
+ </sp-dashboard-chart-selection-panel>
+ }
</div>
</mat-drawer>
<mat-drawer-content class="h-100 dashboard-grid">
@@ -77,6 +79,7 @@
*ngIf="dashboard.widgets.length > 0 && viewMode === 'grid'"
[editMode]="editMode"
[dashboard]="dashboard"
+ [widgets]="widgets"
[timeSettings]="timeSettings"
(deleteCallback)="removeChartFromDashboard($event)"
(startEditModeEmitter)="startEditMode($event)"
@@ -88,6 +91,7 @@
#dashboardSlide
[editMode]="editMode"
[dashboard]="dashboard"
+ [widgets]="widgets"
[timeSettings]="timeSettings"
(deleteCallback)="removeChartFromDashboard($event)"
(startEditModeEmitter)="startEditMode($event)"
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index 5245ab3706..4931bc6417 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -63,6 +63,7 @@ export class DashboardPanelComponent
dashboardLoaded = false;
originalDashboard: Dashboard;
dashboard: Dashboard;
+ widgets: DataExplorerWidgetModel[] = [];
/**
* This is the date range (start, end) to view the data and is set in data-explorer.ts
@@ -73,11 +74,8 @@ export class DashboardPanelComponent
editMode = false;
timeRangeVisible = true;
- @ViewChild('dashboardGrid')
- dashboardGrid: DashboardGridViewComponent;
-
- @ViewChild('dashboardSlide')
- dashboardSlide: DashboardSlideViewComponent;
+ _dashboardGrid: DashboardGridViewComponent;
+ _dashboardSlide: DashboardSlideViewComponent;
hasDataExplorerWritePrivileges = false;
@@ -133,11 +131,14 @@ export class DashboardPanelComponent
dashboardItem.x = 0;
dashboardItem.y = 0;
this.dashboard.widgets.push(dashboardItem);
- if (this.viewMode === 'grid') {
- this.dashboardGrid.loadWidgetConfig(dataViewElementId, true);
- } else {
- this.dashboardSlide.loadWidgetConfig(dataViewElementId, true);
- }
+ setTimeout(() => {
+ if (this.viewMode === 'grid') {
+ console.log(this.dashboardGrid);
+ this.dashboardGrid.loadWidgetConfig(dataViewElementId, true);
+ } else {
+ this.dashboardSlide.loadWidgetConfig(dataViewElementId, true);
+ }
+ });
}
setShouldShowConfirm(): boolean {
@@ -207,42 +208,49 @@ export class DashboardPanelComponent
}
getDashboard(dashboardId: string, startTime: number, endTime: number) {
- this.dashboardService.getDashboard(dashboardId).subscribe(dashboard => {
- this.dashboard = dashboard;
- this.originalDashboard = JSON.parse(JSON.stringify(dashboard));
- this.breadcrumbService.updateBreadcrumb(
- this.breadcrumbService.makeRoute(
- [SpDashboardRoutes.BASE],
- this.dashboard.name,
- ),
- );
- this.viewMode =
- this.dashboard.dashboardGeneralSettings.defaultViewMode ||
- 'grid';
- if (
- this.dashboard.dashboardGeneralSettings.globalTimeEnabled ===
- undefined
- ) {
- this.dashboard.dashboardGeneralSettings.globalTimeEnabled =
- true;
- }
- if (!this.dashboard.dashboardTimeSettings.startTime) {
- this.dashboard.dashboardTimeSettings =
- this.timeSelectionService.getDefaultTimeSettings();
- } else {
- this.timeSelectionService.updateTimeSettings(
- this.timeSelectionService.defaultQuickTimeSelections,
- this.dashboard.dashboardTimeSettings,
- new Date(),
+ this.dashboardService
+ .getCompositeDashboard(dashboardId)
+ .subscribe(compositeDashboard => {
+ this.dashboard = compositeDashboard.dashboard;
+ this.widgets = compositeDashboard.widgets;
+ this.originalDashboard = JSON.parse(
+ JSON.stringify(compositeDashboard.dashboard),
);
- }
- this.timeSettings =
- startTime && endTime
- ? this.overrideTime(+startTime, +endTime)
- : this.dashboard.dashboardTimeSettings;
- this.dashboardLoaded = true;
- this.modifyRefreshInterval(this.dashboard.dashboardLiveSettings);
- });
+ this.breadcrumbService.updateBreadcrumb(
+ this.breadcrumbService.makeRoute(
+ [SpDashboardRoutes.BASE],
+ this.dashboard.name,
+ ),
+ );
+ this.viewMode =
+ this.dashboard.dashboardGeneralSettings.defaultViewMode ||
+ 'grid';
+ if (
+ this.dashboard.dashboardGeneralSettings
+ .globalTimeEnabled === undefined
+ ) {
+ this.dashboard.dashboardGeneralSettings.globalTimeEnabled =
+ true;
+ }
+ if (!this.dashboard.dashboardTimeSettings.startTime) {
+ this.dashboard.dashboardTimeSettings =
+ this.timeSelectionService.getDefaultTimeSettings();
+ } else {
+ this.timeSelectionService.updateTimeSettings(
+ this.timeSelectionService.defaultQuickTimeSelections,
+ this.dashboard.dashboardTimeSettings,
+ new Date(),
+ );
+ }
+ this.timeSettings =
+ startTime && endTime
+ ? this.overrideTime(+startTime, +endTime)
+ : this.dashboard.dashboardTimeSettings;
+ this.dashboardLoaded = true;
+ this.modifyRefreshInterval(
+ this.dashboard.dashboardLiveSettings,
+ );
+ });
}
overrideTime(startTime: number, endTime: number): TimeSettings {
@@ -322,4 +330,26 @@ export class DashboardPanelComponent
)
.subscribe();
}
+
+ @ViewChild('dashboardGrid', { static: false })
+ set dashboardGrid(v: DashboardGridViewComponent) {
+ if (v) {
+ this._dashboardGrid = v;
+ }
+ }
+
+ get dashboardGrid(): DashboardGridViewComponent {
+ return this._dashboardGrid;
+ }
+
+ @ViewChild('dashboardSlide', { static: false })
+ set dashboardSlide(v: DashboardSlideViewComponent) {
+ if (v) {
+ this._dashboardSlide = v;
+ }
+ }
+
+ get dashboardSlide(): DashboardSlideViewComponent {
+ return this._dashboardSlide;
+ }
}
diff --git a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
index 373774462a..fb13faf5bb 100644
--- a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
+++ b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.ts
@@ -31,7 +31,7 @@ import {
} from '@angular/core';
import { GridsterItemComponent } from 'angular-gridster2';
import {
- DashboardItem,
+ ClientDashboardItem,
DataExplorerWidgetModel,
DataLakeMeasure,
ExtendedTimeSettings,
@@ -70,7 +70,7 @@ export class DataExplorerChartContainerComponent
@ViewChild('timeSelectorMenu')
timeSelectorMenu: TimeRangeSelectorMenuComponent;
@Input()
- dashboardItem: DashboardItem;
+ dashboardItem: ClientDashboardItem;
@Input()
configuredWidget: DataExplorerWidgetModel;
diff --git a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
index 5db3fea62a..508dc62ff5 100644
--- a/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
+++ b/ui/src/app/data-explorer-shared/components/charts/base/base-data-explorer-widget.directive.ts
@@ -28,11 +28,10 @@ import {
import { GridsterItem, GridsterItemComponent } from 'angular-gridster2';
import { ChartConfigurationService } from '../../../services/chart-configuration.service';
import {
- DashboardItem,
+ ClientDashboardItem,
DataExplorerDataConfig,
DataExplorerField,
DataExplorerWidgetModel,
- DatalakeRestService,
DataViewQueryGeneratorService,
SpLogMessage,
SpQueryResult,
@@ -80,7 +79,7 @@ export abstract class BaseDataExplorerWidgetDirective<
@Input()
gridMode = true;
- @Input() dataViewDashboardItem: DashboardItem;
+ @Input() dataViewDashboardItem: ClientDashboardItem;
@Input() dataExplorerWidget: T;
@Input()
@@ -110,8 +109,6 @@ export abstract class BaseDataExplorerWidgetDirective<
Observable<SpQueryResult>[]
>();
- // inject services to avoid constructor overload
- protected dataLakeRestService = inject(DatalakeRestService);
protected widgetConfigurationService = inject(ChartConfigurationService);
protected resizeService = inject(ResizeService);
protected dataViewQueryGeneratorService = inject(
diff --git a/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts b/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
index 79875aae08..c66e0cbed2 100644
--- a/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
+++ b/ui/src/app/data-explorer-shared/components/charts/image/image-widget.component.ts
@@ -16,11 +16,12 @@
*
*/
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, inject, OnInit, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { BaseDataExplorerWidgetDirective } from '../base/base-data-explorer-widget.directive';
import {
DataExplorerField,
+ DatalakeRestService,
SpQueryResult,
} from '@streampipes/platform-services';
import { ImageWidgetModel } from './model/image-widget.model';
@@ -45,9 +46,8 @@ export class ImageWidgetComponent
canvasWidth;
imagePreviewHeight;
- constructor(private securePipe: SecurePipe) {
- super();
- }
+ private securePipe = inject(SecurePipe);
+ private dataLakeRestService = inject(DatalakeRestService);
ngOnInit(): void {
super.ngOnInit();
diff --git a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
index ca0d5b9b62..57f4089b1b 100644
--- a/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
+++ b/ui/src/app/data-explorer-shared/models/dataview-dashboard.model.ts
@@ -22,7 +22,7 @@ import {
GridsterItemComponent,
} from 'angular-gridster2';
import {
- DashboardItem,
+ ClientDashboardItem,
DataExplorerField,
DataExplorerWidgetModel,
SpLogMessage,
@@ -50,7 +50,7 @@ export interface BaseWidgetData<T extends DataExplorerWidgetModel> {
timeSettings: TimeSettings;
- dataViewDashboardItem: DashboardItem;
+ dataViewDashboardItem: ClientDashboardItem;
dataExplorerWidget: T;
previewMode: boolean;
gridMode: boolean;