This is an automated email from the ASF dual-hosted git repository.
riemer 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 e2eaa319fb fix: Align UI views with dashboard and data explorer
permissions (#3961)
e2eaa319fb is described below
commit e2eaa319fbc1d655273cf4998bd8935b880d54c7
Author: Dominik Riemer <[email protected]>
AuthorDate: Tue Nov 25 12:48:14 2025 +0100
fix: Align UI views with dashboard and data explorer permissions (#3961)
Co-authored-by: Philipp Zehnder <[email protected]>
---
.../support/utils/dataExplorer/DataExplorerBtns.ts | 15 +-
.../utils/dataExplorer/DataExplorerUtils.ts | 47 ++++-
.../dataExplorer/addAssetToDashboard.smoke.spec.ts | 2 +-
.../testAddAssetOnResourceCreation.ts | 4 +-
.../userManagement/testUserRoleCharts.spec.ts | 54 ++----
.../userManagement/testUserRoleDashboard.spec.ts | 204 +++++++++++++++++++++
.../dashboard-overview-table.component.html | 5 +-
.../chart-selection/chart-selection.component.html | 20 +-
.../chart-selection/chart-selection.component.ts | 12 +-
.../panel/dashboard-panel.component.html | 4 +-
.../components/panel/dashboard-panel.component.ts | 8 +-
.../dashboard-toolbar.component.html | 4 +-
.../dashboard-toolbar.component.ts | 2 +-
.../data-explorer-chart-container.component.html | 32 ++--
.../data-explorer-chart-container.component.ts | 4 +
.../data-explorer-chart-view.component.ts | 26 ++-
.../data-explorer-overview-table.component.ts | 5 +-
.../pipeline-details-toolbar.component.html | 26 +--
.../pipeline-details-toolbar.component.ts | 3 +
.../pipeline-details.component.html | 1 +
.../pipeline-details/pipeline-details.component.ts | 12 +-
21 files changed, 383 insertions(+), 107 deletions(-)
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
index da3de0faef..bbbbf0687f 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerBtns.ts
@@ -25,9 +25,14 @@ export class DataExplorerBtns {
return cy.dataCy('save-data-view-btn', { timeout: 10000 });
}
- public static saveDashboard() {
+ public static saveDataViewBtn() {
return cy.dataCy('save-data-view');
}
+
+ public static saveDashboardBtn() {
+ return cy.dataCy('save-dashboard-btn');
+ }
+
public static saveChartsToAssetBtn() {
return cy
.dataCy('add-to-Asset-data-view-btn', { timeout: 10000 })
@@ -63,10 +68,18 @@ export class DataExplorerBtns {
return cy.dataCy('remove-' + dataViewName);
}
+ public static createChartBtn() {
+ return cy.dataCy('create-chart-button');
+ }
+
public static editDashboardBtn(dashboardName) {
return cy.dataCy('edit-dashboard-' + dashboardName);
}
+ public static viewDashboardBtn(dashboardName) {
+ return cy.dataCy('view-dashboard-' + dashboardName);
+ }
+
public static editDashboardSettingsBtn(dashboardName) {
return cy.dataCy('edit-dashboard-settings-' + dashboardName);
}
diff --git a/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
b/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
index 752816d319..bfc3d20bd1 100644
--- a/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
+++ b/ui/cypress/support/utils/dataExplorer/DataExplorerUtils.ts
@@ -40,7 +40,15 @@ export class DataExplorerUtils {
public static checkAmountOfCharts(amount: number) {
DataExplorerUtils.goToDatalake();
+ this.checkAmount(amount);
+ }
+
+ public static checkAmountOfDashboards(amount: number) {
+ DataExplorerUtils.goToDashboard();
+ this.checkAmount(amount);
+ }
+ private static checkAmount(amount: number) {
if (amount === 0) {
// The wait is needed because the default value is the
no-table-entries element.
// It must be waited till the data is loaded. Once a better
solution is found, this can be removed.
@@ -61,6 +69,16 @@ export class DataExplorerUtils {
DataExplorerBtns.editDataViewButton(chartName).should('not.exist');
}
+ public static checkDashboardCanBeEdited(dashboardName: string) {
+ GeneralUtils.openMenuForRow(dashboardName);
+ DataExplorerBtns.editDashboardBtn(dashboardName).should('exist');
+ }
+
+ public static checkDashboardCanNotBeEdited(dashboardName: string) {
+ GeneralUtils.openMenuForRow(dashboardName);
+ DataExplorerBtns.editDashboardBtn(dashboardName).should('not.exist');
+ }
+
public static initDataLakeTests() {
cy.initStreamPipesTest();
DataExplorerUtils.loadRandomDataSetIntoDataLake();
@@ -161,11 +179,14 @@ export class DataExplorerUtils {
});
}
- public static createDashboard(name) {
- // Create new data view
- DataExplorerBtns.newDashboardDialogBtn().click();
+ public static createNewDashboard(name: string) {
+ DataExplorerUtils.goToDashboard();
+ DataExplorerUtils.addNewDashboard(name);
+ DataExplorerUtils.saveDataView();
+ }
- // Configure data view
+ public static addNewDashboard(name: string) {
+ DataExplorerBtns.newDashboardDialogBtn().click();
cy.dataCy('data-view-name').type(name);
}
@@ -183,14 +204,19 @@ export class DataExplorerUtils {
DataExplorerUtils.goToDashboard();
//ADD Assets
- DataExplorerUtils.createDashboard(name);
+ DataExplorerUtils.addNewDashboard(name);
DataExplorerUtils.addAssetsToDashboard(assetNameList);
- DataExplorerUtils.saveDashboard();
+ DataExplorerUtils.saveDataView();
+ }
+
+ public static saveDataView() {
+ return DataExplorerBtns.saveDataViewBtn().click();
}
public static saveDashboard() {
- return DataExplorerBtns.saveDashboard().click();
+ return DataExplorerBtns.saveDashboardBtn().click();
}
+
public static addDataViewAndTableWidget(
dataViewName: string,
dataSet: string,
@@ -237,7 +263,7 @@ export class DataExplorerUtils {
// Configure data view
cy.dataCy('data-view-name').type(name);
- DataExplorerBtns.saveDashboard().click();
+ DataExplorerBtns.saveDataViewBtn().click();
this.editDashboard(name);
}
@@ -269,6 +295,11 @@ export class DataExplorerUtils {
DataExplorerBtns.editDashboardBtn(dashboardName).click();
}
+ public static viewDashboard(dashboardName: string) {
+ GeneralUtils.openMenuForRow(dashboardName);
+ DataExplorerBtns.viewDashboardBtn(dashboardName).click();
+ }
+
public static editDashboardSettings(dashboardName: string) {
GeneralUtils.openMenuForRow(dashboardName);
DataExplorerBtns.editDashboardSettingsBtn(dashboardName).click();
diff --git a/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
b/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
index defb473209..073243033a 100644
--- a/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
+++ b/ui/cypress/tests/dataExplorer/addAssetToDashboard.smoke.spec.ts
@@ -70,7 +70,7 @@ describe('Test add Assets To Dashboard', () => {
DataExplorerUtils.renameDashboard('NEW');
const assetNameList2 = [assetName2, assetName3];
DataExplorerUtils.addToAsset(assetNameList2);
- DataExplorerUtils.saveDashboard();
+ DataExplorerUtils.saveDataView();
AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName1, 1);
AssetUtils.checkAmountOfLinkedResourcesByAssetName(assetName3, 1);
diff --git a/ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
b/ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
index a27908b732..97c963a015 100644
--- a/ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
+++ b/ui/cypress/tests/userManagement/testAddAssetOnResourceCreation.ts
@@ -123,7 +123,7 @@ describe('Test that resources can be added to assets on
creation', () => {
it('Check Role Asset Admin in Dashboard', () => {
UserUtils.switchUser(newUser);
DataExplorerUtils.goToDashboard();
- DataExplorerUtils.createDashboard('Test');
+ DataExplorerUtils.addNewDashboard('Test');
DataExplorerBtns.dashboardAssetCheckboxBtn().should('exist');
DataExplorerBtns.closeDashboardCreate().click();
@@ -133,7 +133,7 @@ describe('Test that resources can be added to assets on
creation', () => {
UserUtils.switchUser(newUser);
DataExplorerUtils.goToDashboard();
- DataExplorerUtils.createDashboard('Test');
+ DataExplorerUtils.addNewDashboard('Test');
DataExplorerBtns.dashboardAssetCheckboxBtn().should('not.exist');
});
});
diff --git a/ui/cypress/tests/userManagement/testUserRoleCharts.spec.ts
b/ui/cypress/tests/userManagement/testUserRoleCharts.spec.ts
index e1f391afcf..282b5e3324 100644
--- a/ui/cypress/tests/userManagement/testUserRoleCharts.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRoleCharts.spec.ts
@@ -54,12 +54,10 @@ describe('Test User Roles for Charts', () => {
setup();
// check admin
- assertChartIsVisibleAndEditableCanChangePermissions(
- UserUtils.adminUser,
- );
+ chartIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
// check other users
- assertChartIsNotVisible(chartAdmin2);
+ chartIsNotVisible(chartAdmin2);
});
it('Make chart public', () => {
@@ -67,13 +65,11 @@ describe('Test User Roles for Charts', () => {
PermissionUtils.markElementAsPublic(chartName);
- assertChartIsVisibleAndEditableCanChangePermissions(
- UserUtils.adminUser,
- );
+ chartIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
- assertChartIsVisibleButNotEditable(chartUser1);
+ chartIsVisibleButNotEditable(chartUser1);
- assertChartIsVisibleAndEditableCannotChangePermissions(chartAdmin2);
+ chartIsVisibleAndEditableCannotChangePermissions(chartAdmin2);
});
it('Share chart with other user and change ownership', () => {
@@ -81,27 +77,23 @@ describe('Test User Roles for Charts', () => {
PermissionUtils.authorizeUser(chartName, chartAdmin2.email);
- assertChartIsVisibleAndEditableCanChangePermissions(
- UserUtils.adminUser,
- );
+ chartIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
- assertChartIsVisibleAndEditableCannotChangePermissions(chartAdmin2);
+ chartIsVisibleAndEditableCannotChangePermissions(chartAdmin2);
- assertChartIsNotVisible(chartUser1);
+ chartIsNotVisible(chartUser1);
UserUtils.switchUser(chartAdmin1);
DataExplorerUtils.goToDatalake();
PermissionUtils.changeOwnership(chartName, chartAdmin2.email);
- assertChartIsNotVisible(chartAdmin1);
+ chartIsNotVisible(chartAdmin1);
- assertChartIsVisibleAndEditableCanChangePermissions(
- UserUtils.adminUser,
- );
+ chartIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
- assertChartIsVisibleAndEditableCanChangePermissions(chartAdmin2);
+ chartIsVisibleAndEditableCanChangePermissions(chartAdmin2);
- assertChartIsNotVisible(chartUser1);
+ chartIsNotVisible(chartUser1);
});
it('Chart is shared with group for user 2', () => {
@@ -116,13 +108,11 @@ describe('Test User Roles for Charts', () => {
PermissionUtils.authorizeGroup(chartName, chartAdminGroup);
- assertChartIsVisibleAndEditableCanChangePermissions(
- UserUtils.adminUser,
- );
+ chartIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
- assertChartIsNotVisible(chartUser1);
+ chartIsNotVisible(chartUser1);
- assertChartIsVisibleAndEditableCannotChangePermissions(chartAdmin2);
+ chartIsVisibleAndEditableCannotChangePermissions(chartAdmin2);
});
function setup() {
@@ -137,38 +127,32 @@ describe('Test User Roles for Charts', () => {
DataExplorerUtils.goToDatalake();
}
- function assertChartIsVisibleAndEditableCanChangePermissions(user: User) {
+ function chartIsVisibleAndEditableCanChangePermissions(user: User) {
UserUtils.switchUser(user);
- DataExplorerUtils.goToDatalake();
DataExplorerUtils.checkAmountOfCharts(1);
DataExplorerUtils.checkChartCanBeEdited(chartName);
PermissionUtils.validateUserCanChangePermissions(chartName);
}
- function assertChartIsVisibleAndEditableCannotChangePermissions(
- user: User,
- ) {
+ function chartIsVisibleAndEditableCannotChangePermissions(user: User) {
UserUtils.switchUser(user);
- DataExplorerUtils.goToDatalake();
DataExplorerUtils.checkAmountOfCharts(1);
DataExplorerUtils.checkChartCanBeEdited(chartName);
PermissionUtils.validateUserCanNotChangePermissions(chartName);
}
- function assertChartIsVisibleButNotEditable(user: User) {
+ function chartIsVisibleButNotEditable(user: User) {
UserUtils.switchUser(user);
- DataExplorerUtils.goToDatalake();
DataExplorerUtils.checkAmountOfCharts(1);
DataExplorerUtils.checkChartCanNotBeEdited(chartName);
PermissionUtils.validateUserCanNotChangePermissions(chartName);
}
- function assertChartIsNotVisible(user: User) {
+ function chartIsNotVisible(user: User) {
UserUtils.switchUser(user);
- DataExplorerUtils.goToDatalake();
DataExplorerUtils.checkAmountOfCharts(0);
}
});
diff --git a/ui/cypress/tests/userManagement/testUserRoleDashboard.spec.ts
b/ui/cypress/tests/userManagement/testUserRoleDashboard.spec.ts
new file mode 100644
index 0000000000..59c794985a
--- /dev/null
+++ b/ui/cypress/tests/userManagement/testUserRoleDashboard.spec.ts
@@ -0,0 +1,204 @@
+/*
+ * 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 { UserRole } from '../../../src/app/_enums/user-role.enum';
+import { UserUtils } from '../../support/utils/UserUtils';
+import { ConnectUtils } from '../../support/utils/connect/ConnectUtils';
+import { User } from '../../support/model/User';
+import { DataExplorerUtils } from
'../../support/utils/dataExplorer/DataExplorerUtils';
+import { PermissionUtils } from '../../support/utils/user/PermissionUtils';
+import { DataExplorerBtns } from
'../../support/utils/dataExplorer/DataExplorerBtns';
+
+describe('Test User Roles for Dashboards', () => {
+ const dashboardName = 'test-dashboard';
+ let dashboardUser1: User;
+ let dashboardAdmin1: User;
+ let dashboardAdmin2: User;
+
+ beforeEach('Setup Test', () => {
+ cy.initStreamPipesTest();
+
+ dashboardUser1 = UserUtils.createUser(
+ 'dashboardUser1',
+ UserRole.ROLE_DASHBOARD_USER,
+ );
+
+ dashboardAdmin1 = UserUtils.createUser(
+ 'dashboardAdmin1',
+ UserRole.ROLE_DASHBOARD_ADMIN,
+ UserRole.ROLE_DATA_EXPLORER_ADMIN,
+ UserRole.ROLE_CONNECT_ADMIN,
+ UserRole.ROLE_PIPELINE_ADMIN,
+ );
+
+ dashboardAdmin2 = UserUtils.createUser(
+ 'dashboardAdmin2',
+ UserRole.ROLE_DASHBOARD_ADMIN,
+ );
+ });
+
+ it('Dashboard is not shared with other users', () => {
+ UserUtils.switchUser(dashboardAdmin1);
+ DataExplorerUtils.createNewDashboard(dashboardName);
+
+ // check admin
+ dashboardIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
+
+ // check other users
+ dashboardIsNotVisible(dashboardAdmin2);
+ });
+
+ it('Make dashboard public', () => {
+ UserUtils.switchUser(dashboardAdmin1);
+ DataExplorerUtils.createNewDashboard(dashboardName);
+ PermissionUtils.markElementAsPublic(dashboardName);
+
+ dashboardIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
+
+ dashboardIsVisibleButNotEditable(dashboardUser1);
+
+ dashboardIsVisibleAndEditableCannotChangePermissions(dashboardAdmin2);
+ });
+
+ it('Share dashboard with other user and change ownership', () => {
+ UserUtils.switchUser(dashboardAdmin1);
+ DataExplorerUtils.createNewDashboard(dashboardName);
+
+ PermissionUtils.authorizeUser(dashboardName, dashboardAdmin2.email);
+
+ dashboardIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
+
+ dashboardIsVisibleAndEditableCannotChangePermissions(dashboardAdmin2);
+
+ dashboardIsNotVisible(dashboardUser1);
+
+ UserUtils.switchUser(dashboardAdmin1);
+ DataExplorerUtils.goToDashboard();
+ PermissionUtils.changeOwnership(dashboardName, dashboardAdmin2.email);
+
+ dashboardIsNotVisible(dashboardAdmin1);
+
+ dashboardIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
+
+ dashboardIsVisibleAndEditableCanChangePermissions(dashboardAdmin2);
+
+ dashboardIsNotVisible(dashboardUser1);
+ });
+
+ it('Dashboard is shared with group for user 2', () => {
+ const dashboardAdminGroup = 'dashboard_admin_group';
+ UserUtils.createGroup(
+ dashboardAdminGroup,
+ UserRole.ROLE_DASHBOARD_ADMIN,
+ );
+ UserUtils.addGroupToUser(dashboardAdminGroup, dashboardAdmin2.name);
+
+ UserUtils.switchUser(dashboardAdmin1);
+ DataExplorerUtils.createNewDashboard(dashboardName);
+
+ PermissionUtils.authorizeGroup(dashboardName, dashboardAdminGroup);
+
+ dashboardIsVisibleAndEditableCanChangePermissions(UserUtils.adminUser);
+
+ dashboardIsNotVisible(dashboardUser1);
+
+ dashboardIsVisibleAndEditableCannotChangePermissions(dashboardAdmin2);
+ });
+
+ it('Test Dashboard and Data Explorer Permissions', () => {
+ UserUtils.switchUser(dashboardAdmin1);
+
+ ConnectUtils.addMachineDataSimulator('simulator', true);
+ addChart('chart1');
+ cy.wait(1000);
+ addChart('chart2');
+
+ DataExplorerUtils.createNewDashboard(dashboardName);
+
+ DataExplorerUtils.editDashboard(dashboardName);
+ DataExplorerUtils.addDataViewToDashboard('chart1', true);
+ DataExplorerUtils.addDataViewToDashboard('chart2', true);
+ DataExplorerUtils.saveDashboard();
+
+ PermissionUtils.markElementAsPublic(dashboardName);
+
+ UserUtils.switchUser(dashboardAdmin2);
+ DataExplorerUtils.goToDashboard();
+ DataExplorerUtils.viewDashboard(dashboardName);
+ DataExplorerBtns.moreOptionsBtn('chart1').should('exist');
+ DataExplorerBtns.moreOptionsBtn('chart2').should('exist');
+ DataExplorerBtns.removeWidgetBtn('chart1').should('not.exist');
+ DataExplorerBtns.removeWidgetBtn('chart2').should('not.exist');
+
+ DataExplorerUtils.goToDashboard();
+ DataExplorerUtils.editDashboard(dashboardName);
+ DataExplorerBtns.moreOptionsBtn('chart1').should('exist');
+ DataExplorerBtns.moreOptionsBtn('chart2').should('exist');
+ DataExplorerBtns.removeWidgetBtn('chart1').should('exist');
+ DataExplorerBtns.removeWidgetBtn('chart2').should('exist');
+
+ // Validate to add new widget to dashboard
+ DataExplorerBtns.createChartBtn().should('not.exist');
+ DataExplorerBtns.removeWidgetBtn('chart2').click();
+ DataExplorerUtils.saveDashboard();
+
+ UserUtils.switchUser(dashboardUser1);
+ DataExplorerUtils.goToDashboard();
+ DataExplorerUtils.viewDashboard(dashboardName);
+ DataExplorerBtns.moreOptionsBtn('chart1').should('exist');
+ DataExplorerBtns.moreOptionsBtn('chart2').should('not.exist');
+ });
+
+ function dashboardIsVisibleAndEditableCanChangePermissions(user: User) {
+ UserUtils.switchUser(user);
+ DataExplorerUtils.checkAmountOfDashboards(1);
+ DataExplorerUtils.checkDashboardCanBeEdited(dashboardName);
+
+ PermissionUtils.validateUserCanChangePermissions(dashboardName);
+ }
+
+ function dashboardIsVisibleAndEditableCannotChangePermissions(user: User) {
+ UserUtils.switchUser(user);
+ DataExplorerUtils.checkAmountOfDashboards(1);
+ DataExplorerUtils.checkDashboardCanBeEdited(dashboardName);
+
+ PermissionUtils.validateUserCanNotChangePermissions(dashboardName);
+ }
+
+ function dashboardIsVisibleButNotEditable(user: User) {
+ UserUtils.switchUser(user);
+ DataExplorerUtils.checkAmountOfDashboards(1);
+ DataExplorerUtils.checkDashboardCanNotBeEdited(dashboardName);
+
+ PermissionUtils.validateUserCanNotChangePermissions(dashboardName);
+ }
+
+ function dashboardIsNotVisible(user: User) {
+ UserUtils.switchUser(user);
+ DataExplorerUtils.checkAmountOfDashboards(0);
+ }
+
+ function addChart(chartName: string) {
+ DataExplorerUtils.addDataViewAndTableWidget(
+ chartName,
+ 'simulator',
+ true,
+ );
+ DataExplorerUtils.saveDataViewConfiguration();
+ }
+});
diff --git
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
index 4725718082..f7f114bf28 100644
---
a/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
+++
b/ui/src/app/dashboard/components/overview/dashboard-overview-table/dashboard-overview-table.component.html
@@ -91,6 +91,7 @@
<button
mat-menu-item
(click)="showDashboard(element); $event.stopPropagation()"
+ [attr.data-cy]="'view-dashboard-' + element.name"
>
<mat-icon>visibility</mat-icon>
<span>{{ 'Show' | translate }}</span>
@@ -135,9 +136,7 @@
}
<button
mat-menu-item
- [attr.data-cy]="
- 'manage-dashboard-permissions-' + element.name
- "
+ data-cy="open-manage-permissions"
(click)="showPermissionsDialog(element)"
>
<mat-icon>share</mat-icon>
diff --git
a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html
b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html
index 0346b5bbb7..0737ebe5b0 100644
---
a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html
+++
b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.html
@@ -33,14 +33,18 @@
'No charts found - create a new chart first to add it to this
dashboard.'
| translate
}}</span>
- <button
- mat-button
- color="accent"
- class="mt-10"
- (click)="navigateToDataViewCreation()"
- >
- {{ 'Create chart' | translate }}
- </button>
+
+ @if (hasChartWritePrivileges) {
+ <button
+ mat-button
+ color="accent"
+ class="mt-10"
+ data-cy="create-chart-button"
+ (click)="navigateToDataViewCreation()"
+ >
+ {{ 'Create chart' | translate }}
+ </button>
+ }
</div>
}
</div>
diff --git
a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts
b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts
index 220849af4d..76edaa1c7e 100644
---
a/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts
+++
b/ui/src/app/dashboard/components/panel/chart-selection-panel/chart-selection/chart-selection.component.ts
@@ -16,12 +16,14 @@
*
*/
-import { Component, EventEmitter, OnInit, Output } from '@angular/core';
+import { Component, EventEmitter, inject, OnInit, Output } from
'@angular/core';
import {
ChartService,
DataExplorerWidgetModel,
} from '@streampipes/platform-services';
import { Router } from '@angular/router';
+import { AuthService } from '../../../../../services/auth.service';
+import { UserPrivilege } from '../../../../../_enums/user-privilege.enum';
@Component({
selector: 'sp-chart-selection',
@@ -30,11 +32,15 @@ import { Router } from '@angular/router';
standalone: false,
})
export class ChartSelectionComponent implements OnInit {
+ private authService = inject(AuthService);
+
@Output()
addChartEmitter: EventEmitter<string> = new EventEmitter();
charts: DataExplorerWidgetModel[] = [];
+ hasChartWritePrivileges: boolean = false;
+
constructor(
private dataViewService: ChartService,
private router: Router,
@@ -48,6 +54,10 @@ export class ChartSelectionComponent implements OnInit {
),
);
});
+
+ this.hasChartWritePrivileges = this.authService.hasRole(
+ UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW,
+ );
}
navigateToDataViewCreation(): void {
diff --git
a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
index a2aca9c431..e7daf7b908 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
@@ -31,9 +31,7 @@
[dashboard]="dashboard"
[editMode]="editMode"
[(viewMode)]="viewMode"
- [hasDataExplorerWritePrivileges]="
- hasDataExplorerWritePrivileges
- "
+ [hasDashboardWritePrivileges]="hasDashboardWritePrivileges"
[timeRangeVisible]="timeRangeVisible"
[timeSettings]="timeSettings"
(saveDashboardEmitter)="persistDashboardChanges()"
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 0946164609..93ca7bb5fc 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -80,7 +80,7 @@ export class DashboardPanelComponent
_dashboardGrid: DashboardGridViewComponent;
_dashboardSlide: DashboardSlideViewComponent;
- hasDataExplorerWritePrivileges = false;
+ hasDashboardWritePrivileges = false;
public items: Dashboard[];
@@ -114,10 +114,10 @@ export class DashboardPanelComponent
this.getDashboard(params.id, startTime, endTime);
this.authSubscription = this.currentUserService.user$.subscribe(_ => {
- this.hasDataExplorerWritePrivileges = this.authService.hasRole(
- UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW,
+ this.hasDashboardWritePrivileges = this.authService.hasRole(
+ UserPrivilege.PRIVILEGE_WRITE_DASHBOARD,
);
- if (queryParams.editMode && this.hasDataExplorerWritePrivileges) {
+ if (queryParams.editMode && this.hasDashboardWritePrivileges) {
this.editMode = true;
}
});
diff --git
a/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
b/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
index 22f0e0fcfb..0d85549398 100644
---
a/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
+++
b/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.html
@@ -100,7 +100,7 @@
<mat-icon>more_vert</mat-icon>
</button>
<mat-menu #optMenu="matMenu">
- @if (!editMode && hasDataExplorerWritePrivileges) {
+ @if (!editMode && hasDashboardWritePrivileges) {
<button
mat-menu-item
(click)="triggerEditModeEmitter.emit()"
@@ -122,7 +122,7 @@
<span>{{ 'Hide time range selector' | translate }}</span>
</button>
}
- @if (hasDataExplorerWritePrivileges) {
+ @if (hasDashboardWritePrivileges) {
<button mat-menu-item (click)="deleteDashboardEmitter.emit()">
<mat-icon>clear</mat-icon>
<span>{{ 'Delete dashboard' | translate }}</span>
diff --git
a/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.ts
b/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.ts
index 21664ed1aa..9f979d9100 100644
---
a/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.ts
+++
b/ui/src/app/dashboard/components/panel/dashboard-toolbar/dashboard-toolbar.component.ts
@@ -43,7 +43,7 @@ export class DashboardToolbarComponent {
timeRangeVisible: boolean;
@Input()
- hasDataExplorerWritePrivileges: boolean;
+ hasDashboardWritePrivileges: boolean;
@Input()
timeSettings: TimeSettings;
diff --git
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
index 6928d45086..f0fb79ee9e 100644
---
a/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
+++
b/ui/src/app/data-explorer-shared/components/chart-container/data-explorer-chart-container.component.html
@@ -146,21 +146,23 @@
</sp-time-selector-menu>
}
</mat-menu>
- @if (!dataViewMode) {
- @if (editMode && hasDataExplorerWritePrivileges) {
- <button
- mat-icon-button
- (click)="removeWidget()"
- [matTooltip]="'Delete Chart' | translate"
- [attr.data-cy]="
- 'remove-' +
- configuredWidget.baseAppearanceConfig
- .widgetTitle
- "
- >
- <mat-icon>clear</mat-icon>
- </button>
- }
+ @if (
+ !dataViewMode &&
+ editMode &&
+ hasDashboardWritePrivileges
+ ) {
+ <button
+ mat-icon-button
+ (click)="removeWidget()"
+ [matTooltip]="'Delete Chart' | translate"
+ [attr.data-cy]="
+ 'remove-' +
+ configuredWidget.baseAppearanceConfig
+ .widgetTitle
+ "
+ >
+ <mat-icon>clear</mat-icon>
+ </button>
}
</div>
}
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 de5144a8c3..4fb1bb090e 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
@@ -138,6 +138,7 @@ export class DataExplorerChartContainerComponent
};
hasDataExplorerWritePrivileges = false;
+ hasDashboardWritePrivileges = false;
authSubscription: Subscription;
widgetTypeChangedSubscription: Subscription;
@@ -175,6 +176,9 @@ export class DataExplorerChartContainerComponent
this.hasDataExplorerWritePrivileges = this.authService.hasRole(
UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW,
);
+ this.hasDashboardWritePrivileges = this.authService.hasRole(
+ UserPrivilege.PRIVILEGE_WRITE_DASHBOARD,
+ );
},
);
this.widgetLoaded = true;
diff --git
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
index 4935fab6c6..0489ead3bc 100644
---
a/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
+++
b/ui/src/app/data-explorer/components/chart-view/data-explorer-chart-view.component.ts
@@ -20,6 +20,7 @@ import {
Component,
ElementRef,
inject,
+ OnDestroy,
OnInit,
ViewChild,
} from '@angular/core';
@@ -40,6 +41,7 @@ import {
import {
AssetSaveService,
ConfirmDialogComponent,
+ CurrentUserService,
DialogService,
PanelType,
TimeSelectionService,
@@ -48,12 +50,14 @@ import { DataExplorerRoutingService } from
'../../../data-explorer-shared/servic
import { DataExplorerSharedService } from
'../../../data-explorer-shared/services/data-explorer-shared.service';
import { DataExplorerDetectChangesService } from
'../../services/data-explorer-detect-changes.service';
import { SupportsUnsavedChangeDialog } from
'../../../data-explorer-shared/models/dataview-dashboard.model';
-import { Observable, of } from 'rxjs';
+import { Observable, of, Subscription } from 'rxjs';
import { MatDialog } from '@angular/material/dialog';
import { catchError, map } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { ResizeEchartsService } from
'../../../data-explorer-shared/services/resize-echarts.service';
import { AssetDialogComponent } from '../../dialog/asset-dialog.component';
+import { AuthService } from '../../../services/auth.service';
+import { UserRole } from '../../../_enums/user-role.enum';
@Component({
selector: 'sp-data-explorer-data-view',
@@ -62,7 +66,7 @@ import { AssetDialogComponent } from
'../../dialog/asset-dialog.component';
standalone: false,
})
export class DataExplorerChartViewComponent
- implements OnInit, SupportsUnsavedChangeDialog
+ implements OnInit, OnDestroy, SupportsUnsavedChangeDialog
{
dataViewLoaded = false;
timeSettings: TimeSettings;
@@ -91,9 +95,13 @@ export class DataExplorerChartViewComponent
private timeSelectionService = inject(TimeSelectionService);
private translateService = inject(TranslateService);
private dialogService = inject(DialogService);
+ private currentUserService = inject(CurrentUserService);
+ private authService = inject(AuthService);
private assetSaveService = inject(AssetSaveService);
+ currentUser$: Subscription;
+
chartNotFound = false;
observableGenerator =
@@ -103,7 +111,14 @@ export class DataExplorerChartViewComponent
ngOnInit() {
const dataViewId = this.route.snapshot.params.id;
- this.editMode = this.route.snapshot.queryParams.editMode;
+
+ this.currentUser$ = this.currentUserService.user$.subscribe(user => {
+ if (!this.authService.hasRole(UserRole.ROLE_DATA_EXPLORER_ADMIN)) {
+ this.editMode = false;
+ } else {
+ this.editMode = this.route.snapshot.queryParams.editMode;
+ }
+ });
if (dataViewId) {
this.loadDataView(dataViewId);
@@ -342,7 +357,6 @@ export class DataExplorerChartViewComponent
this.deselectedAssets,
this.originalAssets,
);
- //this.dialogRef.close(true);
}
saveToAssets(data: DataExplorerWidgetModel): void {
@@ -364,4 +378,8 @@ export class DataExplorerChartViewComponent
},
];
}
+
+ ngOnDestroy() {
+ this.currentUser$?.unsubscribe();
+ }
}
diff --git
a/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
b/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
index e28ddf8c81..0edf6c1cbf 100644
---
a/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
+++
b/ui/src/app/data-explorer/components/overview/data-explorer-overview-table/data-explorer-overview-table.component.ts
@@ -87,7 +87,10 @@ export class SpDataExplorerDataViewOverviewComponent
implements OnInit {
}
openDataView(dataView: DataExplorerWidgetModel, editMode: boolean): void {
- this.routingService.navigateToDataView(editMode, dataView.elementId);
+ this.routingService.navigateToDataView(
+ editMode && this.hasDataExplorerWritePrivileges,
+ dataView.elementId,
+ );
}
showPermissionsDialog(chart: DataExplorerWidgetModel) {
diff --git
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
index 1df6b32768..7acfa2659c 100644
---
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
+++
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.html
@@ -34,18 +34,20 @@
>
</div>
</button>
- <button
- mat-button
- color="accent"
- [matTooltip]="'Pipeline as code' | translate"
- [matTooltipPosition]="'above'"
- (click)="openCodeDialogEmitter.emit()"
- >
- <div fxLayoutAlign="start center" fxLayout="row">
- <i class="material-icons">code</i>
- <span> {{ 'View pipeline as code' | translate }}</span>
- </div>
- </button>
+ @if (hasPipelineWritePrivileges) {
+ <button
+ mat-button
+ color="accent"
+ [matTooltip]="'Pipeline as code' | translate"
+ [matTooltipPosition]="'above'"
+ (click)="openCodeDialogEmitter.emit()"
+ >
+ <div fxLayoutAlign="start center" fxLayout="row">
+ <i class="material-icons">code</i>
+ <span> {{ 'View pipeline as code' | translate }}</span>
+ </div>
+ </button>
+ }
<div fxFlex></div>
<div fxLayoutAlign="end center">
<button
diff --git
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
index ede7f4eab4..c08ba9108a 100644
---
a/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
+++
b/ui/src/app/pipeline-details/components/pipeline-details-toolbar/pipeline-details-toolbar.component.ts
@@ -27,6 +27,9 @@ export class PipelineDetailsToolbarComponent {
@Input()
autoRefresh: boolean;
+ @Input()
+ hasPipelineWritePrivileges: boolean;
+
@Input()
previewModeActive: boolean;
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.html
b/ui/src/app/pipeline-details/pipeline-details.component.html
index e024d3d147..277b64b496 100644
--- a/ui/src/app/pipeline-details/pipeline-details.component.html
+++ b/ui/src/app/pipeline-details/pipeline-details.component.html
@@ -24,6 +24,7 @@
<div nav fxFlex="100" fxLayoutAlign="start center" class="mr-5">
<sp-pipeline-details-toolbar
fxFlex="100"
+ [hasPipelineWritePrivileges]="hasPipelineWritePrivileges"
[autoRefresh]="autoRefresh"
[previewModeActive]="previewModeActive"
(togglePreviewEmitter)="toggleLivePreview()"
diff --git a/ui/src/app/pipeline-details/pipeline-details.component.ts
b/ui/src/app/pipeline-details/pipeline-details.component.ts
index 1cf208269c..b6edcbaaa8 100644
--- a/ui/src/app/pipeline-details/pipeline-details.component.ts
+++ b/ui/src/app/pipeline-details/pipeline-details.component.ts
@@ -66,8 +66,8 @@ export class SpPipelineDetailsComponent implements OnInit,
OnDestroy {
previewModeActive = false;
pipelineNotFound = false;
- currentUserSub: Subscription;
- autoRefreshSub: Subscription;
+ currentUser$: Subscription;
+ autoRefresh$: Subscription;
@ViewChild('pipelinePreviewComponent')
pipelinePreviewComponent: PipelinePreviewComponent;
@@ -84,7 +84,7 @@ export class SpPipelineDetailsComponent implements OnInit,
OnDestroy {
) {}
ngOnInit(): void {
- this.currentUserSub = this.currentUserService.user$.subscribe(user => {
+ this.currentUser$ = this.currentUserService.user$.subscribe(user => {
this.hasPipelineWritePrivileges = this.authService.hasRole(
UserPrivilege.PRIVILEGE_WRITE_PIPELINE,
);
@@ -147,7 +147,7 @@ export class SpPipelineDetailsComponent implements OnInit,
OnDestroy {
}
setupAutoRefresh(): void {
- this.autoRefreshSub = interval(5000)
+ this.autoRefresh$ = interval(5000)
.pipe(
filter(() => this.autoRefresh),
switchMap(() => this.getMonitoringObservables(true)),
@@ -212,7 +212,7 @@ export class SpPipelineDetailsComponent implements OnInit,
OnDestroy {
}
ngOnDestroy() {
- this.currentUserSub?.unsubscribe();
- this.autoRefreshSub?.unsubscribe();
+ this.currentUser$?.unsubscribe();
+ this.autoRefresh$?.unsubscribe();
}
}