This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch 3725-add-dashboard-kiosk-mode
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit 31c607006ab74afd1cd3e03de416f9c2497b083a
Author: Dominik Riemer <[email protected]>
AuthorDate: Tue Aug 12 11:13:54 2025 +0200

    Add auto-refresh for kiosk mode
---
 .../model/dashboard/CompositeDashboardModel.java   | 10 +++++
 .../impl/dashboard/DataLakeDashboardResource.java  | 24 +++++++++--
 .../src/lib/apis/dashboard.service.ts              | 12 +++++-
 .../src/lib/model/dashboard/dashboard.model.ts     |  1 +
 .../components/kiosk/dashboard-kiosk.component.ts  | 46 +++++++++++++++++++---
 .../grid-view/dashboard-grid-view.component.html   |  4 +-
 .../slide-view/dashboard-slide-view.component.html |  4 +-
 .../components/panel/dashboard-panel.component.ts  | 15 ++++---
 .../data-explorer-chart-container.component.ts     |  2 +-
 .../charts/base/echarts-widget.component.ts        |  3 +-
 .../data-explorer-chart-view.component.html        |  4 +-
 11 files changed, 100 insertions(+), 25 deletions(-)

diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
index cb06527e42..cd14784aaa 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/dashboard/CompositeDashboardModel.java
@@ -22,8 +22,18 @@ import 
org.apache.streampipes.model.datalake.DataExplorerWidgetModel;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 
 import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public record CompositeDashboardModel(DashboardModel dashboard,
                                       List<DataExplorerWidgetModel> widgets,
                                       List<DataLakeMeasure> dataLakeMeasures) {
+
+  public String getRevisionHash() {
+
+    return Stream.concat(
+        Stream.of(dashboard.getRev()),
+        widgets.stream().map(DashboardEntity::getRev)
+    ).collect(Collectors.joining("|"));
+  }
 }
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 f97b7083a1..0fcb6e2162 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
@@ -20,11 +20,12 @@ package org.apache.streampipes.rest.impl.dashboard;
 
 
 import org.apache.streampipes.model.client.user.DefaultPrivilege;
-import org.apache.streampipes.model.dashboard.CompositeDashboardModel;
 import org.apache.streampipes.model.dashboard.DashboardModel;
 import org.apache.streampipes.resource.management.DataExplorerResourceManager;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 
+import org.springframework.http.CacheControl;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PostFilter;
@@ -35,10 +36,12 @@ import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
+import java.util.Objects;
 
 @RestController
 @RequestMapping("/api/v3/datalake/dashboard")
@@ -59,8 +62,23 @@ public class DataLakeDashboardResource extends 
AbstractAuthGuardedRestResource {
 
   @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);
+  public ResponseEntity<?> 
getCompositeDashboardModel(@PathVariable("dashboardId") String dashboardId,
+                                                      @RequestHeader(value = 
"If-None-Match", required = false) String ifNoneMatch) {
+    var dashboard = getResourceManager().getCompositeDashboard(dashboardId);
+    var currentEtag = "\"" + dashboard.getRevisionHash() + "\"";
+    if (Objects.nonNull(ifNoneMatch)) {
+      if (currentEtag.equals(ifNoneMatch)) {
+        return ResponseEntity
+            .status(HttpStatus.NOT_MODIFIED)
+            .eTag(currentEtag)
+            .build();
+      }
+    }
+    return ResponseEntity
+        .ok()
+        .eTag(currentEtag)
+        .cacheControl(CacheControl.noCache())
+        .body(dashboard);
   }
 
   @PutMapping(path = "/{dashboardId}", produces = 
MediaType.APPLICATION_JSON_VALUE)
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 4a40b03218..9539fe2831 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
@@ -16,7 +16,7 @@
  *
  */
 
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpResponse } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 import { SharedDatalakeRestService } from './shared-dashboard.service';
@@ -42,9 +42,17 @@ export class DashboardService {
         return this.http.get<Dashboard>(`${this.dashboardUrl}/${dashboardId}`);
     }
 
-    getCompositeDashboard(dashboardId: string): Observable<CompositeDashboard> 
{
+    getCompositeDashboard(
+        dashboardId: string,
+        eTag = undefined,
+    ): Observable<HttpResponse<any>> {
+        const headers = eTag ? { 'If-None-Match': eTag } : {};
         return this.http.get<CompositeDashboard>(
             `${this.dashboardUrl}/${dashboardId}/composite`,
+            {
+                headers,
+                observe: 'response',
+            },
         );
     }
 
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 927d1a1946..79bd32d7b6 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
@@ -59,4 +59,5 @@ export interface CompositeDashboard {
     dashboard: Dashboard;
     dataLakeMeasures: DataLakeMeasure[];
     widgets: DataExplorerWidgetModel[];
+    revisionHash: string;
 }
diff --git 
a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts 
b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
index 783c6c9090..71b90b579e 100644
--- a/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
+++ b/ui/src/app/dashboard-kiosk/components/kiosk/dashboard-kiosk.component.ts
@@ -1,5 +1,6 @@
 import { Component, inject, OnDestroy, OnInit } from '@angular/core';
 import {
+    CompositeDashboard,
     Dashboard,
     DashboardService,
     DataExplorerWidgetModel,
@@ -24,20 +25,31 @@ export class DashboardKioskComponent implements OnInit, 
OnDestroy {
     dashboard: Dashboard;
     widgets: DataExplorerWidgetModel[] = [];
     refresh$: Subscription;
+    eTag: string;
 
     ngOnInit() {
         const dashboardId = this.route.snapshot.params.dashboardId;
         this.dashboardService
             .getCompositeDashboard(dashboardId)
-            .subscribe(cd => {
-                this.dashboard = cd.dashboard;
-                this.widgets = cd.widgets;
-                if (this.dashboard.dashboardLiveSettings.refreshModeActive) {
-                    this.createQuerySubscription();
+            .subscribe(res => {
+                if (res.ok) {
+                    const cd = res.body;
+                    const eTag = res.headers.get('ETag');
+                    this.initDashboard(cd, eTag);
                 }
             });
     }
 
+    initDashboard(cd: CompositeDashboard, eTag: string): void {
+        this.dashboard = cd.dashboard;
+        this.widgets = cd.widgets;
+        this.eTag = eTag;
+        if (this.dashboard.dashboardLiveSettings.refreshModeActive) {
+            this.createQuerySubscription();
+            this.createRefreshListener();
+        }
+    }
+
     createQuerySubscription() {
         this.refresh$ = timer(
             0,
@@ -58,6 +70,30 @@ export class DashboardKioskComponent implements OnInit, 
OnDestroy {
             .subscribe();
     }
 
+    createRefreshListener(): void {
+        this.dashboardService
+            .getCompositeDashboard(this.dashboard.elementId, this.eTag) // 
this should send If-None-Match
+            .subscribe({
+                next: res => {
+                    if (res.status === 200) {
+                        const newEtag = res.headers.get('ETag');
+                        if (newEtag) {
+                            this.eTag = newEtag;
+                        }
+                        this.dashboard = undefined;
+                        this.refresh$?.unsubscribe();
+                        setTimeout(() => {
+                            this.initDashboard(res.body, newEtag);
+                        });
+                    }
+                    setTimeout(() => this.createRefreshListener(), 5000);
+                },
+                error: err => {
+                    setTimeout(() => this.createRefreshListener(), 5000);
+                },
+            });
+    }
+
     updateDateRange(timeSettings: TimeSettings) {
         let ts = undefined;
         if (this.dashboard.dashboardGeneralSettings.globalTimeEnabled) {
diff --git 
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
 
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
index 9b3bbe4170..fb34956764 100644
--- 
a/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
+++ 
b/ui/src/app/dashboard-shared/components/chart-view/grid-view/dashboard-grid-view.component.html
@@ -35,7 +35,7 @@
                 class="widget-outer"
             >
                 @if (widgetsAvailable && configuredWidgets.has(item.id)) {
-                    <sp-data-explorer-dashboard-widget
+                    <sp-data-explorer-chart-container
                         [ngStyle]="{
                             height: gridsterItemComponent.height - 13 + 'px'
                         }"
@@ -53,7 +53,7 @@
                         [gridMode]="true"
                         [widgetIndex]="i"
                         [gridsterItemComponent]="gridsterItemComponent"
-                    ></sp-data-explorer-dashboard-widget>
+                    ></sp-data-explorer-chart-container>
                 }
             </gridster-item>
         </ng-container>
diff --git 
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
 
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
index 4c73a94118..1640260e3a 100644
--- 
a/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
+++ 
b/ui/src/app/dashboard-shared/components/chart-view/slide-view/dashboard-slide-view.component.html
@@ -48,7 +48,7 @@
         </div>
         <div fxFlex="100">
             <div class="h-100 w-100 mw-100" id="slideViewOuter" fxFlex="100">
-                <sp-data-explorer-dashboard-widget
+                <sp-data-explorer-chart-container
                     [ngStyle]="{
                         height: gridsterItemComponent.height - 15 + 'px'
                     }"
@@ -71,7 +71,7 @@
                         currentWidget &&
                         widgetsVisible
                     "
-                ></sp-data-explorer-dashboard-widget>
+                ></sp-data-explorer-chart-container>
             </div>
         </div>
     </div>
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 f16f0de104..6c6ab6c2da 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -209,12 +209,15 @@ export class DashboardPanelComponent
     getDashboard(dashboardId: string, startTime: number, endTime: number) {
         this.dashboardService
             .getCompositeDashboard(dashboardId)
-            .subscribe(compositeDashboard => {
-                this.dashboard = compositeDashboard.dashboard;
-                this.widgets = compositeDashboard.widgets;
-                this.originalDashboard = JSON.parse(
-                    JSON.stringify(compositeDashboard.dashboard),
-                );
+            .subscribe(resp => {
+                if (resp.ok) {
+                    const compositeDashboard = resp.body;
+                    this.dashboard = compositeDashboard.dashboard;
+                    this.widgets = compositeDashboard.widgets;
+                    this.originalDashboard = JSON.parse(
+                        JSON.stringify(compositeDashboard.dashboard),
+                    );
+                }
                 this.breadcrumbService.updateBreadcrumb(
                     this.breadcrumbService.makeRoute(
                         [SpDashboardRoutes.BASE],
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 d3c1193dac..91f35ac40c 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
@@ -58,7 +58,7 @@ import { DataExplorerSharedService } from 
'../../services/data-explorer-shared.s
 import { MatMenuTrigger } from '@angular/material/menu';
 
 @Component({
-    selector: 'sp-data-explorer-dashboard-widget',
+    selector: 'sp-data-explorer-chart-container',
     templateUrl: './data-explorer-chart-container.component.html',
     styleUrls: ['./data-explorer-chart-container.component.scss'],
     standalone: false,
diff --git 
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
 
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
index a3d366367b..375499beda 100644
--- 
a/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
+++ 
b/ui/src/app/data-explorer-shared/components/charts/base/echarts-widget.component.ts
@@ -125,7 +125,7 @@ export class SpEchartsWidgetComponent<T extends 
DataExplorerWidgetModel>
                 ),
             };
             if (this.kioskMode) {
-                ['legend', 'toolbox', 'visualMap'].forEach(key => {
+                ['toolbox', 'visualMap'].forEach(key => {
                     const item = this.option[key];
                     if (item) {
                         (Array.isArray(item) ? item : [item]).forEach(
@@ -141,7 +141,6 @@ export class SpEchartsWidgetComponent<T extends 
DataExplorerWidgetModel>
                         bottom: 60,
                     },
                 });
-                console.log(this.option);
             }
         } else {
             this.showInvalidConfiguration = true;
diff --git 
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
 
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
index ac7c19b8c4..9bb81bf9b0 100644
--- 
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
+++ 
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.html
@@ -68,7 +68,7 @@
             </mat-drawer>
             <mat-drawer-content class="h-100 dashboard-grid">
                 <div #panel fxFlex="100" fxLayout="column">
-                    <sp-data-explorer-dashboard-widget
+                    <sp-data-explorer-chart-container
                         *ngIf="
                             dataView &&
                             gridsterItemComponent &&
@@ -83,7 +83,7 @@
                         "
                         (startEditModeEmitter)="editDataView()"
                     >
-                    </sp-data-explorer-dashboard-widget>
+                    </sp-data-explorer-chart-container>
                 </div>
             </mat-drawer-content>
         </mat-drawer-container>

Reply via email to