From c5325ed665065b69b7e9cb9a4ffc48aaa6a4c070 Mon Sep 17 00:00:00 2001 From: Dominik Riemer Date: Thu, 29 Aug 2024 15:41:16 +0200 Subject: [PATCH] fix(#3182): Remove data views from dashboards after deletion (#3185) * fix(#3182): Remove data views from dashboards after deletion * fix(#3182): Adapt deleteViewAndDashboard test to validate data view is removed from dashboard * fix(#3182): Remove unused parameter * fix(#3182): Fix linting --------- Co-authored-by: Philipp Zehnder --- .../DataExplorerWidgetResourceManager.java | 18 +++++++++++- .../management/SpResourceManager.java | 5 ++-- .../impl/datalake/DataLakeWidgetResource.java | 10 ++++++- .../support/utils/datalake/DataLakeUtils.ts | 21 ++++++++++---- .../datalake/deleteViewAndDashboard.spec.ts | 28 +++++++++++++------ .../tests/datalake/timeOrderDataView.spec.ts | 2 +- .../tests/datalake/timeRangeSelectors.spec.ts | 2 +- .../apis/data-view-data-explorer.service.ts | 7 +++-- ...ta-explorer-dashboard-panel.component.html | 3 +- .../data-explorer-data-view.component.html | 6 +++- .../data-explorer-data-view.component.ts | 1 - ...a-explorer-dashboard-overview.component.ts | 2 +- ...a-explorer-data-view-overview.component.ts | 3 +- .../abstract-widget-view.directive.ts | 25 ++++++++++------- 14 files changed, 96 insertions(+), 37 deletions(-) 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 9176b3bf43..9ec75d026e 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 @@ -23,7 +23,23 @@ public class DataExplorerWidgetResourceManager extends AbstractCRUDResourceManager { - public DataExplorerWidgetResourceManager(CRUDStorage db) { + private final DataExplorerResourceManager dashboardManager; + + public DataExplorerWidgetResourceManager(DataExplorerResourceManager dashboardManager, + CRUDStorage db) { super(db, DataExplorerWidgetModel.class); + this.dashboardManager = dashboardManager; + } + + @Override + public void delete(String elementId) { + deleteDataViewsFromDashboard(elementId); + super.delete(elementId); + } + + private void deleteDataViewsFromDashboard(String widgetElementId) { + dashboardManager.findAll().stream() + .filter(dashboard -> dashboard.getWidgets().removeIf(w -> w.getId().equals(widgetElementId))) + .forEach(dashboardManager::update); } } diff --git a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java index 5f202f4b81..9e080dfa65 100644 --- a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java +++ b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/SpResourceManager.java @@ -38,8 +38,9 @@ public DataExplorerResourceManager manageDataExplorer() { return new DataExplorerResourceManager(); } - public DataExplorerWidgetResourceManager manageDataExplorerWidget(CRUDStorage db) { - return new DataExplorerWidgetResourceManager(db); + public DataExplorerWidgetResourceManager manageDataExplorerWidget(DataExplorerResourceManager dashboardManager, + CRUDStorage db) { + return new DataExplorerWidgetResourceManager(dashboardManager, db); } public DataProcessorResourceManager manageDataProcessors() { diff --git a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeWidgetResource.java b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeWidgetResource.java index e8743a93a7..099a7502de 100644 --- a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeWidgetResource.java +++ b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/datalake/DataLakeWidgetResource.java @@ -20,10 +20,12 @@ import org.apache.streampipes.model.client.user.Privilege; import org.apache.streampipes.model.datalake.DataExplorerWidgetModel; +import org.apache.streampipes.resource.management.DataExplorerResourceManager; import org.apache.streampipes.resource.management.DataExplorerWidgetResourceManager; import org.apache.streampipes.resource.management.SpResourceManager; import org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource; import org.apache.streampipes.rest.security.AuthConstants; +import org.apache.streampipes.rest.shared.exception.BadRequestException; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -48,6 +50,7 @@ public class DataLakeWidgetResource extends AbstractAuthGuardedRestResource { public DataLakeWidgetResource() { this.resourceManager = new SpResourceManager().manageDataExplorerWidget( + new DataExplorerResourceManager(), getNoSqlStorage().getDataExplorerWidgetStorage() ); } @@ -62,7 +65,12 @@ public List getAllDataExplorerWidgets() { @GetMapping(path = "/{widgetId}", produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("this.hasReadAuthority() and hasPermission(#elementId, 'READ')") public ResponseEntity getDataExplorerWidget(@PathVariable("widgetId") String elementId) { - return ok(resourceManager.find(elementId)); + var widget = resourceManager.find(elementId); + if (widget != null) { + return ok(widget); + } else { + throw new BadRequestException("Could not find widget"); + } } @PutMapping( diff --git a/ui/cypress/support/utils/datalake/DataLakeUtils.ts b/ui/cypress/support/utils/datalake/DataLakeUtils.ts index 396b4c40b8..de72c60baa 100644 --- a/ui/cypress/support/utils/datalake/DataLakeUtils.ts +++ b/ui/cypress/support/utils/datalake/DataLakeUtils.ts @@ -147,15 +147,20 @@ export class DataLakeUtils { this.editDashboard(name); } - public static addDataViewToDashboard(dataViewName: string) { - this.selectTimeRange( - new Date(2020, 10, 20, 22, 44), - this.getFutureDate(), - ); + public static addDataViewToDashboard( + dataViewName: string, + ignoreTimeRange = false, + ) { + if (!ignoreTimeRange) { + this.selectTimeRange( + new Date(2020, 10, 20, 22, 44), + this.getFutureDate(), + ); + } cy.dataCy('add-data-view-btn-' + dataViewName).click(); } - public static createAndEditDataView(name: string) { + public static createAndEditDataView() { // Create new data view cy.dataCy('open-new-data-view').click(); } @@ -184,6 +189,10 @@ export class DataLakeUtils { cy.dataCy('save-dashboard-btn', { timeout: 10000 }).click(); } + public static getEmptyDashboardInformation() { + return cy.dataCy('empty-dashboard'); + } + public static deleteDashboard(dashboardName: string) { cy.dataCy('delete-dashboard-' + dashboardName, { timeout: 10000, diff --git a/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts b/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts index 742a576813..fb4526f187 100644 --- a/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts +++ b/ui/cypress/tests/datalake/deleteViewAndDashboard.spec.ts @@ -24,13 +24,18 @@ describe('Test Deletion of Data View and Dashboard', () => { }); it('Perform Test', () => { + const dashboard = 'TestDashboard'; + const dataView = 'TestView'; + DataLakeUtils.goToDatalake(); - DataLakeUtils.addDataViewAndTableWidget('TestView', 'Persist'); + DataLakeUtils.addDataViewAndTableWidget(dataView, 'Persist'); DataLakeUtils.saveDataViewConfiguration(); - DataLakeUtils.createAndEditDashboard('TestDashboard'); + DataLakeUtils.createAndEditDashboard(dashboard); + + DataLakeUtils.addDataViewToDashboard(dataView, true); DataLakeUtils.saveDashboardConfiguration(); @@ -39,20 +44,27 @@ describe('Test Deletion of Data View and Dashboard', () => { DataLakeUtils.checkRowsViewsTable(1); // Click "Delete" but cancel action and check if dashboard and view are still displayed - DataLakeUtils.cancelDeleteDashboard('TestDashboard'); + DataLakeUtils.cancelDeleteDashboard(dashboard); DataLakeUtils.checkRowsDashboardTable(1); - DataLakeUtils.cancelDeleteDataView('TestView'); + DataLakeUtils.cancelDeleteDataView(dataView); DataLakeUtils.checkRowsViewsTable(1); - DataLakeUtils.deleteDashboard('TestDashboard'); + DataLakeUtils.deleteDataView(dataView); - DataLakeUtils.deleteDataView('TestView'); + DataLakeUtils.checkRowsViewsTable(0); - DataLakeUtils.checkRowsDashboardTable(0); + DataLakeUtils.editDashboard(dashboard); - DataLakeUtils.checkRowsViewsTable(0); + // Validate that data view is removed from dashboard + DataLakeUtils.getEmptyDashboardInformation().should('be.visible'); + + DataLakeUtils.saveDashboardConfiguration(); + + DataLakeUtils.deleteDashboard(dashboard); + + DataLakeUtils.checkRowsDashboardTable(0); }); }); diff --git a/ui/cypress/tests/datalake/timeOrderDataView.spec.ts b/ui/cypress/tests/datalake/timeOrderDataView.spec.ts index ded730c5df..1b1ee0f222 100644 --- a/ui/cypress/tests/datalake/timeOrderDataView.spec.ts +++ b/ui/cypress/tests/datalake/timeOrderDataView.spec.ts @@ -24,7 +24,7 @@ describe('Test Time Order in Data Explorer', () => { cy.initStreamPipesTest(); DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv', false); DataLakeUtils.goToDatalake(); - DataLakeUtils.createAndEditDataView('TestView'); + DataLakeUtils.createAndEditDataView(); }); it('Perform Test with ascending and descending order', () => { diff --git a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts index 68525a5f24..e2a8faff84 100644 --- a/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts +++ b/ui/cypress/tests/datalake/timeRangeSelectors.spec.ts @@ -45,7 +45,7 @@ describe('Test Time Range Selectors in Data Explorer', () => { cy.initStreamPipesTest(); DataLakeUtils.loadDataIntoDataLake('datalake/sample.csv', false); DataLakeUtils.goToDatalake(); - DataLakeUtils.createAndEditDataView('TestView'); + DataLakeUtils.createAndEditDataView(); }); it('Perform Test', () => { diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts index 320432ad4a..51e71213c6 100644 --- a/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts +++ b/ui/projects/streampipes/platform-services/src/lib/apis/data-view-data-explorer.service.ts @@ -16,8 +16,8 @@ * */ -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { catchError, Observable, throwError } from 'rxjs'; import { map } from 'rxjs/operators'; import { Dashboard } from '../model/dashboard/dashboard.model'; import { Injectable } from '@angular/core'; @@ -91,6 +91,9 @@ export class DataViewDataExplorerService { getWidget(widgetId: string): Observable { return this.http.get(this.dashboardWidgetUrl + '/' + widgetId).pipe( + catchError(() => { + return throwError(() => new Error('Failed to get widget data')); + }), map(response => { return DataExplorerWidgetModel.fromData( response as DataExplorerWidgetModel, diff --git a/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html b/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html index b98fe933a4..437e4004b4 100644 --- a/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html +++ b/ui/src/app/data-explorer/components/dashboard/data-explorer-dashboard-panel.component.html @@ -63,9 +63,10 @@ fxFlex="100" fxLayout="column" fxLayoutAlign="center center" + data-cy="empty-dashboard" >

- This data view is empty and doesn't contain any widgets. + This dashboard is empty and doesn't contain any widgets.

- this.dataViewDataExplorerService.getWidget(w.id), + this.dataViewDataExplorerService + .getWidget(w.id) + .pipe(catchError(() => of(undefined))), ); zip(...observables).subscribe(results => { results.forEach(r => { @@ -117,14 +120,16 @@ export abstract class AbstractWidgetViewDirective { } processWidget(widget: DataExplorerWidgetModel) { - widget.widgetType = this.widgetRegistryService.getWidgetType( - widget.widgetType, - ); - this.configuredWidgets.set(widget.elementId, widget); - this.dataLakeMeasures.set( - widget.elementId, - widget.dataConfig.sourceConfigs[0].measure, - ); + if (widget !== undefined) { + widget.widgetType = this.widgetRegistryService.getWidgetType( + widget.widgetType, + ); + this.configuredWidgets.set(widget.elementId, widget); + this.dataLakeMeasures.set( + widget.elementId, + widget.dataConfig.sourceConfigs[0].measure, + ); + } } propagateItemRemoval(widgetIndex: number) {