This is an automated email from the ASF dual-hosted git repository.
zehnder pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new 79cb92a1c0 Add / Edit Assets during Dashboard Creation Edit (#3846)
79cb92a1c0 is described below
commit 79cb92a1c0262b9da6be8ddef7fc2d516bad8eea
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Fri Oct 17 16:32:50 2025 +0200
Add / Edit Assets during Dashboard Creation Edit (#3846)
Co-authored-by: Philipp Zehnder <[email protected]>
Co-authored-by: Dominik Riemer <[email protected]>
---
.../impl/dashboard/DataLakeDashboardResource.java | 7 +-
.../support/utils/dataExplorer/DataExplorerBtns.ts | 4 ++
.../utils/dataExplorer/DataExplorerUtils.ts | 60 ++++++++++++++++
.../dataExplorer/addAssetToDashboard.smoke.spec.ts | 81 +++++++++++++++++++++
.../dataExplorer/addAssetsToDataView.smoke.spec.ts | 61 ++++++++++++++++
.../edit-dashboard-dialog.component.html | 53 +++++++++++---
.../edit-dashboard-dialog.component.ts | 83 ++++++++++++++++++++--
.../dialog/asset-dialog.component.html | 2 +-
8 files changed, 333 insertions(+), 18 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 ec015f2390..b4944312be 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
@@ -104,11 +104,12 @@ public class DataLakeDashboardResource extends
AbstractAuthGuardedRestResource {
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("this.hasWriteAuthority()")
- public ResponseEntity<Void> createDashboard(@RequestBody DashboardModel
dashboardModel) {
- getResourceManager().create(dashboardModel, getAuthenticatedUserSid());
- return ok();
+ public ResponseEntity<DashboardModel> createDashboard(@RequestBody
DashboardModel dashboardModel) {
+ var response = getResourceManager().create(dashboardModel,
getAuthenticatedUserSid());
+ return ok(response);
}
+
private DataExplorerResourceManager getResourceManager() {
return getSpResourceManager().manageDataExplorer();
}
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
index 35b3ca1b66..429c785b04 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
@@ -27,6 +27,10 @@ export class DataLakeBtns {
return cy.dataCy('save-data-view-btn').click();
}
+ public static saveDashboard() {
+ return cy.dataCy('save-data-view').click();
+ }
+
public static editDataViewButton(widgetName: string) {
GeneralUtils.openMenuForRow(widgetName);
return cy
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
b/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
index 3bdaf3a855..db51364d14 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
@@ -114,7 +114,54 @@ export class DataExplorerUtils {
cy.wait(1000);
}
+ public static addAssetsToDashboard(assetNameList) {
+ cy.dataCy('sp-show-dashboard-asset-checkbox')
+ .find('input[type="checkbox"]')
+ .then($checkbox => {
+ if (!$checkbox.prop('checked')) {
+ cy.wrap($checkbox).click();
+ }
+ });
+ cy.get('mat-tree.asset-tree', { timeout: 10000 }).should('exist');
+ assetNameList.forEach(assetName => {
+ console.log(assetName);
+ cy.get('mat-tree.asset-tree')
+ .find('.mat-tree-node')
+ .contains(assetName)
+ .click();
+ });
+ }
+
+ public static createDashboard(name) {
+ // Create new data view
+ cy.dataCy('open-new-dashboard-dialog').click();
+
+ // Configure data view
+ cy.dataCy('data-view-name').type(name);
+ }
+ public static createDashboardWithLinkedAssets(
+ dataView,
+ name,
+ assetNameList,
+ ) {
+ DataExplorerUtils.goToDatalake();
+
+ DataExplorerUtils.addDataViewAndTableWidget(dataView, 'Persist');
+
+ DataExplorerUtils.saveDataViewConfiguration();
+
+ DataExplorerUtils.goToDashboard();
+
+ //ADD Assets
+ DataExplorerUtils.createDashboard(name);
+ DataExplorerUtils.addAssetsToDashboard(assetNameList);
+ DataExplorerUtils.saveDashboard();
+ }
+
+ public static saveDashboard() {
+ return cy.dataCy('save-data-view').click();
+ }
public static addDataViewAndTableWidget(
dataViewName: string,
dataSet: string,
@@ -144,6 +191,11 @@ export class DataExplorerUtils {
);
}
+ public static renameDashboard(newName: string) {
+ cy.dataCy('data-view-name').clear().type(newName);
+ cy.dataCy('data-view-name').should('have.value', newName);
+ }
+
public static loadRandomDataSetIntoDataLake() {
PrepareTestDataUtils.loadDataIntoDataLake('fileTest/random.csv');
}
@@ -186,6 +238,11 @@ export class DataExplorerUtils {
cy.dataCy('edit-dashboard-' + dashboardName).click();
}
+ public static editDashboardSettings(dashboardName: string) {
+ GeneralUtils.openMenuForRow(dashboardName);
+ cy.dataCy('edit-dashboard-settings-' + dashboardName).click();
+ }
+
public static editDataView(dataViewName: string) {
// Click edit button
// following only works if single view is available
@@ -225,7 +282,9 @@ export class DataExplorerUtils {
.contains(assetName)
.click();
});
+ }
+ public static saveAssetLinkFromChart() {
cy.dataCy('asset-dialog-confirm-delete', { timeout: 10000 }).click({
force: true,
});
@@ -606,5 +665,6 @@ export class DataExplorerUtils {
//Save
DataExplorerUtils.saveToAddAssets();
DataExplorerUtils.addToAsset(assetNames);
+ DataExplorerUtils.saveAssetLinkFromChart();
}
}
diff --git a/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
b/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
new file mode 100644
index 0000000000..defb473209
--- /dev/null
+++ b/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+import { AssetBtns } from '../../support/utils/asset/AssetBtns';
+import { AssetUtils } from '../../support/utils/asset/AssetUtils';
+import { DataExplorerUtils } from
'../../support/utils/dataExplorer/DataExplorerUtils';
+
+describe('Test add Assets To Dashboard', () => {
+ const assetName1 = 'TestAsset1';
+ const assetName2 = 'TestAsset2';
+ const assetName3 = 'TestAsset3';
+ beforeEach('Setup Test', () => {
+ cy.initStreamPipesTest();
+ AssetUtils.goToAssets();
+ AssetUtils.addAndSaveAsset(assetName3);
+ AssetUtils.addAndSaveAsset(assetName2);
+ AssetUtils.addAndSaveAsset(assetName1);
+ DataExplorerUtils.loadDataIntoDataLake('datalake/sample.csv');
+ });
+
+ it('Create Dashboard and add Assets', () => {
+ const dataView = 'TestView';
+
+ const name = 'Dashboard1';
+
+ const assetNameList = [assetName1, assetName2];
+ DataExplorerUtils.createDashboardWithLinkedAssets(
+ dataView,
+ name,
+ assetNameList,
+ );
+
+ //Go Back to Asset
+ AssetUtils.goToAssets();
+ AssetUtils.checkAmountOfAssetsGreaterThan(0);
+
+ AssetUtils.editAsset(assetName1);
+ AssetBtns.assetLinksTab().click();
+
+ //Check if Link is there
+ AssetUtils.checkAmountOfLinkedResources(1);
+ });
+
+ it('Edit Dashboard and edit Asset Links', () => {
+ const dataView = 'TestView';
+
+ const name = 'Dashboard1';
+
+ const assetNameList = [assetName1, assetName2];
+ DataExplorerUtils.createDashboardWithLinkedAssets(
+ dataView,
+ name,
+ assetNameList,
+ );
+ DataExplorerUtils.editDashboardSettings(name);
+ DataExplorerUtils.renameDashboard('NEW');
+ const assetNameList2 = [assetName2, assetName3];
+ DataExplorerUtils.addToAsset(assetNameList2);
+ DataExplorerUtils.saveDashboard();
+
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName1, 1);
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName3, 1);
+
+ // Test Renaming
+ AssetUtils.checkResourceNamingByAssetName(assetName1, 'NEW');
+ });
+});
diff --git a/ui/cypress/tests/dataExplorer/addAssetsToDataView.smoke.spec.ts
b/ui/cypress/tests/dataExplorer/addAssetsToDataView.smoke.spec.ts
new file mode 100644
index 0000000000..beb3d5f5c6
--- /dev/null
+++ b/ui/cypress/tests/dataExplorer/addAssetsToDataView.smoke.spec.ts
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { AssetUtils } from '../../support/utils/asset/AssetUtils';
+import { DataExplorerUtils } from
'../../support/utils/dataExplorer/DataExplorerUtils';
+
+describe('Creates a new adapter with a linked asset', () => {
+ const assetName1 = 'TestAsset1';
+ const assetName2 = 'TestAsset2';
+ const assetName3 = 'TestAsset3';
+
+ beforeEach('Setup Test', () => {
+ cy.initStreamPipesTest();
+ AssetUtils.goToAssets();
+ AssetUtils.addAndSaveAsset(assetName3);
+ AssetUtils.addAndSaveAsset(assetName2);
+ AssetUtils.addAndSaveAsset(assetName1);
+ });
+
+ it('Add Assets during Chart generation', () => {
+ DataExplorerUtils.createDataViewWithAssets([assetName1, assetName2]);
+ //Test
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName1, 1);
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName2, 1);
+ });
+
+ it('Edit Assets during Chart generation', () => {
+ DataExplorerUtils.createDataViewWithAssets([assetName1, assetName2]);
+ //Test
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName1, 1);
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName2, 1);
+
+ // Go To Chart and Edit
+ DataExplorerUtils.goToDatalake();
+ DataExplorerUtils.editDataView('NewWidget');
+ DataExplorerUtils.renameWidget('Rename');
+
+ DataExplorerUtils.saveToAddAssets();
+ DataExplorerUtils.addToAsset([assetName1, assetName3]);
+ DataExplorerUtils.saveAssetLinkFromChart();
+
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName2, 1);
+ AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName3, 1);
+ AssetUtils.checkResourceNamingByAssetName(assetName2, 'Rename');
+ });
+});
diff --git
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
index 69d5054e40..3da870685e 100644
---
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
+++
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
@@ -97,20 +97,47 @@
}}
</mat-checkbox>
</div>
+
+ <div class="mt-10" fxLayout="column">
+ <label>{{ 'Add Dashboard to Assets' | translate }}</label>
+
+ <mat-checkbox
+ [(ngModel)]="addToAssets"
+ color="accent"
+ data-cy="sp-show-dashboard-asset-checkbox"
+ >
+ {{
+ 'Add the current dashboard to an existing asset'
+ | translate
+ }}
+ </mat-checkbox>
+
+ @if (addToAssets) {
+ <div class="mt-10">
+ <sp-asset-link-configuration
+ [isEdit]="!createMode"
+ [itemId]="dashboard.elementId"
+ (selectedAssetsChange)="
+ onSelectedAssetsChange($event)
+ "
+ (deselectedAssetsChange)="
+ onDeselectedAssetsChange($event)
+ "
+ (originalAssetsEmitter)="
+ onOriginalAssetsEmitted($event)
+ "
+ >
+ </sp-asset-link-configuration>
+ </div>
+ }
+ </div>
+
<!--<mat-checkbox [(ngModel)]="dashboard.displayHeader">Show
name and description in dashboard</mat-checkbox>-->
</div>
</div>
</div>
<mat-divider></mat-divider>
- <div class="sp-dialog-actions actions-align-right">
- <button
- mat-button
- mat-flat-button
- class="mat-basic mr-10"
- (click)="onCancel()"
- >
- {{ 'Close' | translate }}
- </button>
+ <div class="sp-dialog-actions actions-align-left">
<button
[disabled]="dvname.invalid"
mat-button
@@ -121,5 +148,13 @@
>
{{ (createMode ? 'Create' : 'Save') | translate }}
</button>
+ <button
+ mat-button
+ mat-flat-button
+ class="mat-basic mr-10"
+ (click)="onCancel()"
+ >
+ {{ 'Close' | translate }}
+ </button>
</div>
</div>
diff --git
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
index 112e70cc41..426b676362 100644
---
a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
+++
b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.ts
@@ -16,9 +16,21 @@
*
*/
-import { Component, inject, Input, OnInit } from '@angular/core';
-import { Dashboard, DashboardService } from '@streampipes/platform-services';
-import { DialogRef } from '@streampipes/shared-ui';
+import {
+ Component,
+ EventEmitter,
+ inject,
+ Input,
+ OnInit,
+ Output,
+} from '@angular/core';
+import {
+ Dashboard,
+ DashboardService,
+ LinkageData,
+ SpAssetTreeNode,
+} from '@streampipes/platform-services';
+import { AssetSaveService, DialogRef } from '@streampipes/shared-ui';
@Component({
selector: 'sp-edit-dashboard-dialog-component',
@@ -29,9 +41,19 @@ import { DialogRef } from '@streampipes/shared-ui';
export class EditDashboardDialogComponent implements OnInit {
@Input() createMode: boolean;
@Input() dashboard: Dashboard;
+ @Input() selectedAssets: SpAssetTreeNode[];
+ @Input() deselectedAssets: SpAssetTreeNode[];
+ @Input() originalAssets: SpAssetTreeNode[];
+
+ @Output() selectedAssetsChange = new EventEmitter<SpAssetTreeNode[]>();
+ @Output() deselectedAssetsChange = new EventEmitter<SpAssetTreeNode[]>();
+ @Output() originalAssetsChange = new EventEmitter<SpAssetTreeNode[]>();
private dialogRef = inject(DialogRef<EditDashboardDialogComponent>);
private dashboardService = inject(DashboardService);
+ private assetSaveService = inject(AssetSaveService);
+
+ addToAssets: boolean = false;
ngOnInit() {
if (!this.dashboard.dashboardGeneralSettings.defaultViewMode) {
@@ -43,24 +65,75 @@ export class EditDashboardDialogComponent implements OnInit
{
) {
this.dashboard.dashboardGeneralSettings.globalTimeEnabled = true;
}
+ if (!this.createMode) {
+ this.addToAssets = true;
+ }
}
onCancel(): void {
this.dialogRef.close();
}
+ onSelectedAssetsChange(updatedAssets: SpAssetTreeNode[]): void {
+ this.selectedAssets = updatedAssets;
+ this.selectedAssetsChange.emit(this.selectedAssets);
+ }
+
+ onDeselectedAssetsChange(updatedAssets: SpAssetTreeNode[]): void {
+ this.deselectedAssets = updatedAssets;
+ this.deselectedAssetsChange.emit(this.deselectedAssets);
+ }
+
+ onOriginalAssetsEmitted(updatedAssets: SpAssetTreeNode[]): void {
+ this.originalAssets = updatedAssets;
+ this.originalAssetsChange.emit(this.originalAssets);
+ }
+
+ saveToAssets(data): void {
+ let linkageData: LinkageData[];
+ try {
+ linkageData = this.createLinkageData(data);
+
+ this.saveAssets(linkageData);
+ } catch (err) {
+ console.error('Error in addToAsset:', err);
+ }
+ }
+
+ private createLinkageData(data): LinkageData[] {
+ return [
+ {
+ type: 'dashboard',
+ id: data.elementId,
+ name: data.name,
+ },
+ ];
+ }
+
+ private async saveAssets(linkageData: LinkageData[]): Promise<void> {
+ await this.assetSaveService.saveSelectedAssets(
+ this.selectedAssets,
+ linkageData,
+ this.deselectedAssets,
+ this.originalAssets,
+ );
+ this.dialogRef.close(true);
+ }
+
onSave(): void {
this.dashboard.metadata.lastModifiedEpochMs = Date.now();
if (this.createMode) {
this.dashboardService
.saveDashboard(this.dashboard)
- .subscribe(() => {
+ .subscribe(data => {
+ this.saveToAssets(data);
this.dialogRef.close();
});
} else {
this.dashboardService
.updateDashboard(this.dashboard)
- .subscribe(() => {
+ .subscribe(data => {
+ this.saveToAssets(data);
this.dialogRef.close();
});
}
diff --git a/ui/src/app/data-explorer/dialog/asset-dialog.component.html
b/ui/src/app/data-explorer/dialog/asset-dialog.component.html
index bab116961a..4f99200ef6 100644
--- a/ui/src/app/data-explorer/dialog/asset-dialog.component.html
+++ b/ui/src/app/data-explorer/dialog/asset-dialog.component.html
@@ -25,7 +25,7 @@
color="accent"
data-cy="sp-show-chart-asset-checkbox"
>
- Add Pipeline to Assets
+ Add Chart to Assets
</mat-checkbox>
@if (addToAssets) {
<div class="mt-10">