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; + } }
