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

dominikriemer pushed a commit to branch add-resource-dtos
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit 14ab362a1082fe045bde47951e111853fe9d8a58
Author: Dominik Riemer <[email protected]>
AuthorDate: Fri Jun 12 21:45:50 2026 +0200

    Improve robustness
---
 .../model/datalake/ChartSummaryDto.java            |   7 +-
 .../DataExplorerWidgetResourceManager.java         |  30 +++-
 .../lib/model/resource/resource-summary.model.ts   |   6 +-
 .../chart-overview-table.component.html            |   4 +-
 .../chart-overview-table.component.ts              | 166 +++++++++++++--------
 5 files changed, 138 insertions(+), 75 deletions(-)

diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/ChartSummaryDto.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/ChartSummaryDto.java
index 859f8b7b09..eb6f6035a5 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/ChartSummaryDto.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/ChartSummaryDto.java
@@ -20,8 +20,9 @@ package org.apache.streampipes.model.datalake;
 
 public record ChartSummaryDto(String elementId,
                               String name,
-                              long createdAtEpochMs,
-                              long lastModifiedEpochMs,
+                              Long createdAtEpochMs,
+                              Long lastModifiedEpochMs,
                               String widgetType,
-                              boolean multiSourceChart) {
+                              boolean multiSourceChart,
+                              DataExplorerWidgetHealthStatus healthStatus) {
 }
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerWidgetResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerWidgetResourceManager.java
index 37831266e0..7d6b841d1e 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerWidgetResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/DataExplorerWidgetResourceManager.java
@@ -43,11 +43,12 @@ public class DataExplorerWidgetResourceManager extends 
CrudResourceManager<DataE
         .filter(chart -> permissionEvaluator.hasPermission(auth, 
chart.getElementId(), "READ"))
         .map(chart -> new ChartSummaryDto(
             chart.getElementId(),
-            chart.getBaseAppearanceConfig().get("widgetTitle").toString(),
-            chart.getMetadata().getCreatedAtEpochMs(),
-            chart.getMetadata().getLastModifiedEpochMs(),
+            getChartName(chart),
+            getCreatedAt(chart),
+            getLastModified(chart),
             chart.getWidgetType(),
-            isMultiSourceChart(chart)
+            isMultiSourceChart(chart),
+            chart.getHealthStatus()
         ))
         .toList();
 
@@ -79,4 +80,25 @@ public class DataExplorerWidgetResourceManager extends 
CrudResourceManager<DataE
 
     return false;
   }
+
+  private String getChartName(DataExplorerWidgetModel chart) {
+    if (chart == null || chart.getBaseAppearanceConfig() == null) {
+      return chart != null ? chart.getElementId() : null;
+    }
+
+    Object widgetTitle = chart.getBaseAppearanceConfig().get("widgetTitle");
+    return widgetTitle != null ? widgetTitle.toString() : chart.getElementId();
+  }
+
+  private Long getCreatedAt(DataExplorerWidgetModel chart) {
+    return chart != null && chart.getMetadata() != null
+        ? chart.getMetadata().getCreatedAtEpochMs()
+        : null;
+  }
+
+  private Long getLastModified(DataExplorerWidgetModel chart) {
+    return chart != null && chart.getMetadata() != null
+        ? chart.getMetadata().getLastModifiedEpochMs()
+        : null;
+  }
 }
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts
index 4f7f73e047..28b9aa3457 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/resource/resource-summary.model.ts
@@ -17,6 +17,7 @@
  */
 
 import { PipelineHealthStatus } from '../gen/streampipes-model';
+import { DataExplorerWidgetHealthStatus } from '../gen/streampipes-model';
 
 export interface ResourceSummaryDto<T> {
     totalCount: number;
@@ -45,8 +46,9 @@ export interface PipelineSummaryDto {
 export interface ChartSummaryDto {
     elementId: string;
     name: string;
-    createdAtEpochMs: number;
-    lastModifiedEpochMs: number;
+    createdAtEpochMs: number | null;
+    lastModifiedEpochMs: number | null;
     multiSourceChart: boolean;
     widgetType: string;
+    healthStatus: DataExplorerWidgetHealthStatus;
 }
diff --git 
a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
 
b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
index f1462eab3b..c12569b4d2 100644
--- 
a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
+++ 
b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.html
@@ -98,7 +98,7 @@
                 <td mat-cell *matCellDef="let element">
                     @if (element.lastModifiedEpochMs !== null) {
                         <div>
-                            {{ this.formatDate(element.lastModifiedEpochMs) }}
+                            {{ formatDate(element.lastModifiedEpochMs) }}
                         </div>
                     } @else {
                         <div>–</div>
@@ -113,7 +113,7 @@
                 <td mat-cell *matCellDef="let element">
                     @if (element.createdAtEpochMs !== null) {
                         <div>
-                            {{ this.formatDate(element.createdAtEpochMs) }}
+                            {{ formatDate(element.createdAtEpochMs) }}
                         </div>
                     } @else {
                         <div>–</div>
diff --git 
a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts
 
b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts
index 3ea21c05ec..0968a4a169 100644
--- 
a/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts
+++ 
b/ui/src/app/chart/components/chart-overview/chart-overview-table/chart-overview-table.component.ts
@@ -16,7 +16,14 @@
  *
  */
 
-import { Component, inject, Input, OnInit, ViewChild } from '@angular/core';
+import {
+    Component,
+    inject,
+    Input,
+    OnDestroy,
+    OnInit,
+    ViewChild,
+} from '@angular/core';
 import {
     MatCell,
     MatCellDef,
@@ -89,7 +96,7 @@ type ManageableChart = DataExplorerWidgetModel & {
         TranslatePipe,
     ],
 })
-export class ChartOverviewTableComponent implements OnInit {
+export class ChartOverviewTableComponent implements OnInit, OnDestroy {
     @Input()
     hasDataExplorerWritePrivileges: boolean;
 
@@ -123,6 +130,10 @@ export class ChartOverviewTableComponent implements OnInit 
{
 
     assetFilter$: Subscription;
     currentFilterIds = new Set<string>();
+    private chartTypeMetadata = new Map<
+        string,
+        { icon: string; label: string }
+    >();
 
     ngOnInit(): void {
         this.assetFilterService.applyAssetLinkType('chart');
@@ -156,6 +167,10 @@ export class ChartOverviewTableComponent implements OnInit 
{
         });
     }
 
+    ngOnDestroy(): void {
+        this.assetFilter$?.unsubscribe();
+    }
+
     openChart(dataView: ChartSummaryDto, editMode: boolean): void {
         this.routingService.navigateToChart(
             editMode && this.hasDataExplorerWritePrivileges,
@@ -164,62 +179,60 @@ export class ChartOverviewTableComponent implements 
OnInit {
     }
 
     showManageDialog(chartSummary: ChartSummaryDto) {
-        this.dataViewService
-            .getChart(chartSummary.elementId)
-            .subscribe(chart => {
-                const resource: ManageableChart = {
-                    ...chart,
-                    baseAppearanceConfig: { ...chart.baseAppearanceConfig },
-                    name: chart.baseAppearanceConfig.widgetTitle,
-                    description: '',
+        this.withChart(chartSummary, chart => {
+            const resource: ManageableChart = {
+                ...chart,
+                baseAppearanceConfig: { ...chart.baseAppearanceConfig },
+                name: chart.baseAppearanceConfig.widgetTitle,
+                description: '',
+            };
+            const resourceConfig: 
ObjectManageDialogResourceConfig<ManageableChart> =
+                {
+                    resourceLabel: 'Chart',
+                    nameLabel: 'Chart title',
+                    descriptionLabel: 'Chart description',
+                    nameProperty: 'name',
+                    assetLinkType: 'chart',
+                    assetLinkCheckboxLabel:
+                        'Add the current chart to an existing asset',
+                    saveResource: resource => {
+                        resource.baseAppearanceConfig.widgetTitle =
+                            resource.name;
+                        const chartResource: Partial<ManageableChart> = {
+                            ...resource,
+                        };
+                        delete chartResource.name;
+                        delete chartResource.description;
+                        return this.dataViewService.updateChart(
+                            chartResource as DataExplorerWidgetModel,
+                        );
+                    },
                 };
-                const resourceConfig: 
ObjectManageDialogResourceConfig<ManageableChart> =
-                    {
-                        resourceLabel: 'Chart',
-                        nameLabel: 'Chart title',
-                        descriptionLabel: 'Chart description',
-                        nameProperty: 'name',
-                        assetLinkType: 'chart',
-                        assetLinkCheckboxLabel:
-                            'Add the current chart to an existing asset',
-                        saveResource: resource => {
-                            resource.baseAppearanceConfig.widgetTitle =
-                                resource.name;
-                            const chartResource: Partial<ManageableChart> = {
-                                ...resource,
-                            };
-                            delete chartResource.name;
-                            delete chartResource.description;
-                            return this.dataViewService.updateChart(
-                                chartResource as DataExplorerWidgetModel,
-                            );
-                        },
-                    };
 
-                const dialogRef = this.dialogService.open(
-                    ObjectManageDialogComponent,
-                    {
-                        panelType: PanelType.SLIDE_IN_PANEL,
-                        title: this.translateService.instant('Manage'),
-                        width: '50vw',
-                        data: {
-                            objectInstanceId: chart.elementId,
-                            resource,
-                            saveMode: 'immediate',
-                            resourceConfig,
-                            headerTitle:
-                                this.translateService.instant('Manage Chart ') 
+
-                                chart.baseAppearanceConfig.widgetTitle,
-                        },
+            const dialogRef = this.dialogService.open(
+                ObjectManageDialogComponent,
+                {
+                    panelType: PanelType.SLIDE_IN_PANEL,
+                    title: this.translateService.instant('Manage'),
+                    width: '50vw',
+                    data: {
+                        objectInstanceId: chart.elementId,
+                        resource,
+                        saveMode: 'immediate',
+                        resourceConfig,
+                        headerTitle:
+                            this.translateService.instant('Manage Chart ') +
+                            chart.baseAppearanceConfig.widgetTitle,
                     },
-                );
+                },
+            );
 
-                dialogRef.afterClosed().subscribe(refresh => {
-                    if (refresh) {
-                        this.getCharts();
-                    }
-                });
+            dialogRef.afterClosed().subscribe(refresh => {
+                if (refresh) {
+                    this.getCharts();
+                }
             });
+        });
     }
 
     deleteChart(chart: ChartSummaryDto) {
@@ -251,13 +264,11 @@ export class ChartOverviewTableComponent implements 
OnInit {
     }
 
     cloneChart(chartSummary: ChartSummaryDto) {
-        this.dataViewService
-            .getChart(chartSummary.elementId)
-            .subscribe(chart => {
-                this.dataViewService.cloneChart(chart).subscribe(() => {
-                    this.getCharts();
-                });
+        this.withChart(chartSummary, chart => {
+            this.dataViewService.cloneChart(chart).subscribe(() => {
+                this.getCharts();
             });
+        });
     }
 
     applyChartFilters(elementIds: Set<string>): void {
@@ -275,13 +286,11 @@ export class ChartOverviewTableComponent implements 
OnInit {
     }
 
     getChartTypeIcon(chart: ChartSummaryDto): string {
-        return this.chartRegistryService.getChartTemplate(chart.widgetType)
-            .icon;
+        return this.getChartTypeMetadata(chart.widgetType).icon;
     }
 
     getChartTypeName(chart: ChartSummaryDto): string {
-        return this.chartRegistryService.getChartTemplate(chart.widgetType)
-            .label;
+        return this.getChartTypeMetadata(chart.widgetType).label;
     }
 
     formatDate(timestamp?: number): string {
@@ -292,7 +301,36 @@ export class ChartOverviewTableComponent implements OnInit 
{
         return chart.multiSourceChart;
     }
 
-    requiresAttention(chart: DataExplorerWidgetModel): boolean {
+    requiresAttention(chart: ChartSummaryDto): boolean {
         return chart?.healthStatus === 'REQUIRES_ATTENTION';
     }
+
+    private withChart(
+        chartSummary: ChartSummaryDto,
+        callback: (chart: DataExplorerWidgetModel) => void,
+    ): void {
+        this.dataViewService
+            .getChart(chartSummary.elementId)
+            .subscribe(chart => {
+                callback(chart);
+            });
+    }
+
+    private getChartTypeMetadata(widgetType: string): {
+        icon: string;
+        label: string;
+    } {
+        const cached = this.chartTypeMetadata.get(widgetType);
+        if (cached) {
+            return cached;
+        }
+
+        const template = 
this.chartRegistryService.getChartTemplate(widgetType);
+        const metadata = {
+            icon: template?.icon ?? 'insert_chart',
+            label: template?.label ?? widgetType,
+        };
+        this.chartTypeMetadata.set(widgetType, metadata);
+        return metadata;
+    }
 }

Reply via email to