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 223aa0ad94 fix(#2651): incorrect redirect to dashboard instead of app 
module (#2689)
223aa0ad94 is described below

commit 223aa0ad948103a6ce068bb9946df41599660b1a
Author: Philipp Zehnder <[email protected]>
AuthorDate: Fri Apr 5 17:02:46 2024 +0200

    fix(#2651): incorrect redirect to dashboard instead of app module (#2689)
    
    * fix(#2651): Fix app module and deactivate it for default installation
    
    * fix(#2651): Refactor and deprecate AssetDashboardResource
    
    * fix(#2651): Add sanitize file name to FileManger
    
    * fix(#2651): Check for file type in file manager
    
    * fix(#2651): Check for file type in AssetDashboardResource
    
    * fix(#2651): Fix e2e test for user roles
---
 .../streampipes/manager/file/FileManager.java      |  55 ++++++++--
 .../streampipes/manager/file/TestFileManager.java  | 112 +++++++++++++++++----
 .../rest/impl/AssetDashboardResource.java          |  57 ++++++++---
 .../apache/streampipes/sdk/helpers/Filetypes.java  |   9 +-
 .../streampipes/sdk/helpers/FiletypesTest.java}    |  31 +++---
 .../testVariousUserRoles.smoke.spec.ts             |   2 +-
 ui/deployment/dev/config.yml                       |   1 -
 .../app-asset-monitoring.component.html            |   2 +-
 .../app-asset-monitoring.module.ts                 |   4 +-
 ui/src/app/app-overview/app-overview.module.ts     |   4 +-
 10 files changed, 209 insertions(+), 68 deletions(-)

diff --git 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
index 192d124bbe..5038ec3af0 100644
--- 
a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
+++ 
b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/file/FileManager.java
@@ -60,22 +60,33 @@ public class FileManager {
                                        String filename,
                                        InputStream fileInputStream) throws 
IOException {
 
-    String filetype = filename.substring(filename.lastIndexOf(".") + 1);
+    var filetype = filename.substring(filename.lastIndexOf(".") + 1);
 
-    fileInputStream = cleanFile(fileInputStream, filetype);
+    fileInputStream = validateFileNameAndCleanFile(filename, filetype, 
fileInputStream);
 
-    FileMetadata fileMetadata = makeFileMetadata(user, filename, filetype);
-    new FileHandler().storeFile(filename, fileInputStream);
-    storeFileMetadata(fileMetadata);
-    return fileMetadata;
+    var sanitizedFilename = sanitizeFilename(filename);
+
+    writeToFile(sanitizedFilename, fileInputStream);
+
+    return makeAndStoreFileMetadata(user, sanitizedFilename, filetype);
   }
 
+
   public static void deleteFile(String id) {
     FileMetadata fileMetadata = getFileMetadataStorage().getMetadataById(id);
     new FileHandler().deleteFile(fileMetadata.getFilename());
     getFileMetadataStorage().deleteFileMetadata(id);
   }
 
+  private static InputStream validateFileNameAndCleanFile(String filename,
+                                                  String filetype,
+                                                  InputStream fileInputStream) 
{
+    if (!validateFileType(filename)) {
+      throw new IllegalArgumentException("Filetype for file %s not 
allowed".formatted(filename));
+    }
+
+    return cleanFile(fileInputStream, filetype);
+  }
 
   /**
    * Remove Byte Order Mark (BOM) from csv files
@@ -84,7 +95,7 @@ public class FileManager {
    * @param filetype file of type
    * @return input stream without BOM
    */
-  public static InputStream cleanFile(InputStream fileInputStream, String 
filetype) {
+  protected static InputStream cleanFile(InputStream fileInputStream, String 
filetype) {
     if (Filetypes.CSV.getFileExtensions().contains(filetype.toLowerCase())) {
       fileInputStream = new BOMInputStream(fileInputStream);
     }
@@ -97,10 +108,21 @@ public class FileManager {
     return !fileHash.equals(hash);
   }
 
-  private static void storeFileMetadata(FileMetadata fileMetadata) {
-    getFileMetadataStorage().addFileMetadata(fileMetadata);
+  public static String sanitizeFilename(String filename) {
+    return filename.replaceAll("[^a-zA-Z0-9.\\-]", "_");
+  }
+
+  public static boolean validateFileType(String filename) {
+    return Filetypes.getAllFileExtensions()
+                    .stream()
+                    .anyMatch(filename::endsWith);
+  }
+
+  protected static void writeToFile(String sanitizedFilename, InputStream 
fileInputStream) throws IOException {
+    new FileHandler().storeFile(sanitizedFilename, fileInputStream);
   }
 
+
   private static IFileMetadataStorage getFileMetadataStorage() {
     return StorageDispatcher
         .INSTANCE
@@ -108,6 +130,15 @@ public class FileManager {
         .getFileMetadataStorage();
   }
 
+  protected static FileMetadata makeAndStoreFileMetadata(String user,
+                                                       String 
sanitizedFilename,
+                                                       String filetype) {
+    var fileMetadata = makeFileMetadata(user, sanitizedFilename, filetype);
+    storeFileMetadata(fileMetadata);
+
+    return fileMetadata;
+  }
+
   private static FileMetadata makeFileMetadata(String user,
                                                String filename,
                                                String filetype) {
@@ -121,6 +152,11 @@ public class FileManager {
     return fileMetadata;
   }
 
+  private static void storeFileMetadata(FileMetadata fileMetadata) {
+    getFileMetadataStorage().addFileMetadata(fileMetadata);
+  }
+
+
   private static List<FileMetadata> filterFiletypes(List<FileMetadata> 
allFiles, String filetypes) {
     return allFiles
         .stream()
@@ -129,4 +165,5 @@ public class FileManager {
             .anyMatch(ft -> ft.equals(fileMetadata.getFiletype())))
         .collect(Collectors.toList());
   }
+
 }
diff --git 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
index 58553e72f6..a02a309fa3 100644
--- 
a/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
+++ 
b/streampipes-pipeline-management/src/test/java/org/apache/streampipes/manager/file/TestFileManager.java
@@ -18,46 +18,116 @@
 package org.apache.streampipes.manager.file;
 
 import org.apache.commons.io.IOUtils;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
 public class TestFileManager {
 
+  @Test
+  public void storeFile_throwsExceptionForInvalidFileType() {
+    var filename = "testFile.invalid";
+
+    assertThrows(IllegalArgumentException.class, () ->
+        FileManager.storeFile("", filename, mock(InputStream.class)));
+  }
+
   @Test
   public void testCleanFileWithoutBom() throws IOException {
-    String expected = "test";
-    InputStream inputStream = IOUtils.toInputStream(expected, 
StandardCharsets.UTF_8);
-    InputStream resultStream = FileManager.cleanFile(inputStream, "CSV");
-    String resultString = IOUtils.toString(resultStream, 
StandardCharsets.UTF_8);
+    var expected = "test";
+    var inputStream = IOUtils.toInputStream(expected, StandardCharsets.UTF_8);
+    var resultStream = FileManager.cleanFile(inputStream, "CSV");
+    var resultString = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
 
-    Assertions.assertEquals(expected, resultString);
+    assertEquals(expected, resultString);
   }
 
   @Test
   public void testCleanFileWithBom() throws IOException {
-    String expected = "test";
-    String utf8Bom = "\uFEFF";
-    String inputString = utf8Bom + expected;
-    InputStream inputStream = IOUtils.toInputStream(inputString, 
StandardCharsets.UTF_8);
-    InputStream resultStream = FileManager.cleanFile(inputStream, "CSV");
-    String resultString = IOUtils.toString(resultStream, 
StandardCharsets.UTF_8);
+    var expected = "test";
+    var utf8Bom = "\uFEFF";
+    var inputString = utf8Bom + expected;
+    var inputStream = IOUtils.toInputStream(inputString, 
StandardCharsets.UTF_8);
+    var resultStream = FileManager.cleanFile(inputStream, "CSV");
+    var resultString = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
 
-    Assertions.assertEquals(expected, resultString);
+    assertEquals(expected, resultString);
   }
 
   @Test
   public void testCleanFileWithBomAndUmlauts() throws IOException {
-    String expected = "testäüö";
-    String utf8Bom = "\uFEFF";
-    String inputString = utf8Bom + expected;
-    InputStream inputStream = IOUtils.toInputStream(inputString, 
StandardCharsets.UTF_8);
-    InputStream resultStream = FileManager.cleanFile(inputStream, "CSV");
-    String resultString = IOUtils.toString(resultStream, 
StandardCharsets.UTF_8);
-
-    Assertions.assertEquals(expected, resultString);
+    var expected = "testäüö";
+    var utf8Bom = "\uFEFF";
+    var inputString = utf8Bom + expected;
+    var inputStream = IOUtils.toInputStream(inputString, 
StandardCharsets.UTF_8);
+    var resultStream = FileManager.cleanFile(inputStream, "CSV");
+    var resultString = IOUtils.toString(resultStream, StandardCharsets.UTF_8);
+
+    assertEquals(expected, resultString);
+  }
+
+  @Test
+  public void 
sanitizeFilename_replacesNonAlphanumericCharactersWithUnderscore() {
+    var filename = "file@name#with$special%characters";
+    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    assertEquals("file_name_with_special_characters", sanitizedFilename);
+  }
+
+  @Test
+  public void sanitizeFilename_keepsAlphanumericAndDotAndHyphenCharacters() {
+    var filename = "file.name-with_alphanumeric123";
+    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    assertEquals(filename, sanitizedFilename);
+  }
+
+  @Test
+  public void 
sanitizeFilename_returnsUnderscoreForFilenameWithAllSpecialCharacters() {
+    var filename = "@#$%^&*()";
+    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    assertEquals("_________", sanitizedFilename);
+  }
+
+  @Test
+  public void sanitizeFilename_returnsEmptyStringForEmptyFilename() {
+    var filename = "";
+    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    assertEquals("", sanitizedFilename);
+  }
+
+  @Test
+  public void sanitizeFilename_removesSingleParentDirectory() {
+    var filename = "../file.csv";
+    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    assertEquals(".._file.csv", sanitizedFilename);
+  }
+
+  @Test
+  public void sanitizeFilename_removesDoubleParentDirectoryy() {
+    var filename = "../../file";
+    var sanitizedFilename = FileManager.sanitizeFilename(filename);
+    assertEquals(".._.._file", sanitizedFilename);
+  }
+
+  @Test
+  public void validateFileName_returnsTrueForCsv() {
+    assertTrue(FileManager.validateFileType("file.csv"));
+  }
+
+  @Test
+  public void validateFileName_returnsTrueForJson() {
+    assertTrue(FileManager.validateFileType("file.json"));
+  }
+
+  @Test
+  public void validateFileName_returnsFalseForSh() {
+    assertFalse(FileManager.validateFileType("file.sh"));
   }
 }
\ No newline at end of file
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
index 919bea60d9..4433d51ae4 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/AssetDashboardResource.java
@@ -17,6 +17,7 @@
  */
 package org.apache.streampipes.rest.impl;
 
+import org.apache.streampipes.manager.file.FileManager;
 import org.apache.streampipes.model.client.assetdashboard.AssetDashboardConfig;
 import org.apache.streampipes.rest.core.base.impl.AbstractRestResource;
 import org.apache.streampipes.rest.shared.exception.SpMessageException;
@@ -43,6 +44,7 @@ import org.springframework.web.multipart.MultipartFile;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.FileNameMap;
 import java.net.URLConnection;
 import java.nio.file.Files;
@@ -51,6 +53,7 @@ import java.util.List;
 
 @RestController
 @RequestMapping("/api/v2/asset-dashboards")
+@Deprecated(forRemoval = true, since = "0.95.0")
 public class AssetDashboardResource extends AbstractRestResource {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(AssetDashboardResource.class);
@@ -59,44 +62,52 @@ public class AssetDashboardResource extends 
AbstractRestResource {
 
   @GetMapping(path = "/{dashboardId}", produces = 
MediaType.APPLICATION_JSON_VALUE)
   public ResponseEntity<AssetDashboardConfig> 
getAssetDashboard(@PathVariable("dashboardId") String dashboardId) {
-    return 
ok(getNoSqlStorage().getAssetDashboardStorage().getAssetDashboard(dashboardId));
+    return ok(getNoSqlStorage().getAssetDashboardStorage()
+                               .getAssetDashboard(dashboardId));
   }
 
   @PutMapping(
       path = "/{dashboardId}",
       produces = MediaType.APPLICATION_JSON_VALUE,
       consumes = MediaType.APPLICATION_JSON_VALUE)
-  public ResponseEntity<Void> 
updateAssetDashboard(@PathVariable("dashboardId") String dashboardId,
-                                                   @RequestBody 
AssetDashboardConfig dashboardConfig) {
+  public ResponseEntity<Void> updateAssetDashboard(
+      @PathVariable("dashboardId") String dashboardId,
+      @RequestBody AssetDashboardConfig dashboardConfig
+  ) {
     AssetDashboardConfig dashboard = 
getAssetDashboardStorage().getAssetDashboard(dashboardId);
     dashboardConfig.setRev(dashboard.getRev());
-    
getNoSqlStorage().getAssetDashboardStorage().updateAssetDashboard(dashboardConfig);
+    getNoSqlStorage().getAssetDashboardStorage()
+                     .updateAssetDashboard(dashboardConfig);
     return ok();
   }
 
   @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
   public ResponseEntity<List<AssetDashboardConfig>> getAllDashboards() {
-    return 
ok(getNoSqlStorage().getAssetDashboardStorage().getAllAssetDashboards());
+    return ok(getNoSqlStorage().getAssetDashboardStorage()
+                               .getAllAssetDashboards());
   }
 
   @PostMapping(
       produces = MediaType.APPLICATION_JSON_VALUE,
       consumes = MediaType.APPLICATION_JSON_VALUE)
   public ResponseEntity<Void> storeAssetDashboard(@RequestBody 
AssetDashboardConfig dashboardConfig) {
-    
getNoSqlStorage().getAssetDashboardStorage().storeAssetDashboard(dashboardConfig);
+    getNoSqlStorage().getAssetDashboardStorage()
+                     .storeAssetDashboard(dashboardConfig);
     return ok();
   }
 
   @DeleteMapping(path = "/{dashboardId}")
   public ResponseEntity<Void> 
deleteAssetDashboard(@PathVariable("dashboardId") String dashboardId) {
-    
getNoSqlStorage().getAssetDashboardStorage().deleteAssetDashboard(dashboardId);
+    getNoSqlStorage().getAssetDashboardStorage()
+                     .deleteAssetDashboard(dashboardId);
     return ok();
   }
 
   @GetMapping(path = "/images/{imageName}")
   public ResponseEntity<byte[]> getDashboardImage(@PathVariable("imageName") 
String imageName) {
     try {
-      java.nio.file.Path path = Paths.get(getTargetFile(imageName));
+      var sanitizedFileName = FileManager.sanitizeFilename(imageName);
+      java.nio.file.Path path = Paths.get(getTargetFile(sanitizedFileName));
       File file = new File(path.toString());
       FileNameMap fileNameMap = URLConnection.getFileNameMap();
       String mimeType = fileNameMap.getContentTypeFor(file.getName());
@@ -114,16 +125,35 @@ public class AssetDashboardResource extends 
AbstractRestResource {
 
   @PostMapping(path = "/images", consumes = 
MediaType.MULTIPART_FORM_DATA_VALUE)
   public ResponseEntity<Void> storeDashboardImage(@RequestPart("file_upload") 
MultipartFile fileDetail) {
-    File targetDirectory = new File(getTargetDirectory());
+    try {
+      var fileName = fileDetail.getName();
+
+      if (!FileManager.validateFileType(fileName)) {
+        LOG.error("File type of file " + fileName + " is not supported");
+        fail();
+      }
+
+      storeFile(fileDetail.getName(), fileDetail.getInputStream());
+      return ok();
+    } catch (IOException e) {
+      LOG.error("Could not extract image input stream from request", e);
+      return fail();
+    }
+  }
+
+  private void storeFile(String fileName, InputStream fileInputStream) {
+
+    var targetDirectory = new File(getTargetDirectory());
     if (!targetDirectory.exists()) {
       targetDirectory.mkdirs();
     }
 
-    File targetFile = new File(getTargetFile(fileDetail.getName()));
+    var sanitizedFileName = FileManager.sanitizeFilename(fileName);
+
+    var targetFile = new File(getTargetFile(sanitizedFileName));
 
     try {
-      FileUtils.copyInputStreamToFile(fileDetail.getInputStream(), targetFile);
-      return ok();
+      FileUtils.copyInputStreamToFile(fileInputStream, targetFile);
     } catch (IOException e) {
       LOG.error(e.getMessage(), e);
       throw new SpMessageException(HttpStatus.INTERNAL_SERVER_ERROR, e);
@@ -140,6 +170,7 @@ public class AssetDashboardResource extends 
AbstractRestResource {
   }
 
   private IAssetDashboardStorage getAssetDashboardStorage() {
-    return 
StorageDispatcher.INSTANCE.getNoSqlStore().getAssetDashboardStorage();
+    return StorageDispatcher.INSTANCE.getNoSqlStore()
+                                     .getAssetDashboardStorage();
   }
 }
diff --git 
a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
 
b/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
index 2ac00e6c45..681b180983 100644
--- 
a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
+++ 
b/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
@@ -19,6 +19,7 @@ package org.apache.streampipes.sdk.helpers;
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Stream;
 
 public enum Filetypes {
 
@@ -30,7 +31,7 @@ public enum Filetypes {
   XML("xml"),
   ZIP("zip");
 
-  private List<String> fileExtensions;
+  private final List<String> fileExtensions;
 
   Filetypes(String... fileExtensions) {
     this.fileExtensions = Arrays.asList(fileExtensions);
@@ -39,4 +40,10 @@ public enum Filetypes {
   public List<String> getFileExtensions() {
     return fileExtensions;
   }
+
+  public static List<String> getAllFileExtensions() {
+    return Stream.of(Filetypes.values())
+        .flatMap(filetype -> filetype.getFileExtensions().stream())
+        .toList();
+  }
 }
diff --git 
a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
 
b/streampipes-sdk/src/test/java/org/apache/streampipes/sdk/helpers/FiletypesTest.java
similarity index 62%
copy from 
streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
copy to 
streampipes-sdk/src/test/java/org/apache/streampipes/sdk/helpers/FiletypesTest.java
index 2ac00e6c45..8c287f2cbc 100644
--- 
a/streampipes-sdk/src/main/java/org/apache/streampipes/sdk/helpers/Filetypes.java
+++ 
b/streampipes-sdk/src/test/java/org/apache/streampipes/sdk/helpers/FiletypesTest.java
@@ -15,28 +15,23 @@
  * limitations under the License.
  *
  */
-package org.apache.streampipes.sdk.helpers;
 
-import java.util.Arrays;
-import java.util.List;
+package org.apache.streampipes.sdk.helpers;
 
-public enum Filetypes {
+import org.junit.jupiter.api.Test;
 
-  CSV("csv"),
-  JPG("jpg", "jpeg"),
-  JSON("json"),
-  XLS("xls"),
-  XLSX("xlsx"),
-  XML("xml"),
-  ZIP("zip");
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-  private List<String> fileExtensions;
+class FiletypesTest {
 
-  Filetypes(String... fileExtensions) {
-    this.fileExtensions = Arrays.asList(fileExtensions);
-  }
+  @Test
+  public void getAllFileExtensions_returnsAllExtensions() {
+    var actualExtensions = Filetypes.getAllFileExtensions();
 
-  public List<String> getFileExtensions() {
-    return fileExtensions;
+    assertEquals(8, actualExtensions.size());
+    assertTrue(actualExtensions.contains("jpg"));
+    assertTrue(actualExtensions.contains("jpeg"));
+    assertTrue(actualExtensions.contains("csv"));
   }
-}
+}
\ No newline at end of file
diff --git a/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts 
b/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts
index 1d5195b1da..d64bfef8d7 100644
--- a/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts
+++ b/ui/cypress/tests/userManagement/testVariousUserRoles.smoke.spec.ts
@@ -40,7 +40,7 @@ for (var i = 0; i < testedRoles.length; i++) {
             UserUtils.goToUserConfiguration();
             cy.dataCy('navigation-icon', { timeout: 10000 }).should(
                 'have.length',
-                11,
+                10,
             );
 
             cy.dataCy('user-accounts-table-row', { timeout: 10000 }).should(
diff --git a/ui/deployment/dev/config.yml b/ui/deployment/dev/config.yml
index d3980f177b..4edd9074ee 100644
--- a/ui/deployment/dev/config.yml
+++ b/ui/deployment/dev/config.yml
@@ -23,7 +23,6 @@ modules:
     - spConnect
     - spDashboard
     - spDataExplorer
-    - spAppOverview
     - spAdd
     - spAssets
     - spFiles
diff --git 
a/ui/src/app/app-asset-monitoring/app-asset-monitoring.component.html 
b/ui/src/app/app-asset-monitoring/app-asset-monitoring.component.html
index 4ec241b38a..7fe7439dc4 100644
--- a/ui/src/app/app-asset-monitoring/app-asset-monitoring.component.html
+++ b/ui/src/app/app-asset-monitoring/app-asset-monitoring.component.html
@@ -16,7 +16,7 @@
   ~
   -->
 
-<sp-basic-view showBackLink="true" [backLinkTarget]="['apps']">
+<sp-basic-view showBackLink="true" [backLinkTarget]="['apps', 'overview']">
     <ng-container nav>
         <mat-tab-group
             [selectedIndex]="selectedIndex"
diff --git a/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts 
b/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts
index cac83cb9cd..df9d4bfe46 100644
--- a/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts
+++ b/ui/src/app/app-asset-monitoring/app-asset-monitoring.module.ts
@@ -33,12 +33,12 @@ import { ColorPickerModule } from 'ngx-color-picker';
 import { SaveDashboardDialogComponent } from 
'./dialog/save-dashboard/save-dashboard-dialog.component';
 import { AssetDashboardOverviewComponent } from 
'./components/dashboard-overview/dashboard-overview.component';
 import { AddLinkDialogComponent } from 
'./dialog/add-link/add-link-dialog.component';
-import { DashboardModule } from '../dashboard/dashboard.module';
 import { RouterModule } from '@angular/router';
 import { SharedUiModule } from '@streampipes/shared-ui';
 import { MatDividerModule } from '@angular/material/divider';
 import { MatIconModule } from '@angular/material/icon';
 import { MatTabsModule } from '@angular/material/tabs';
+import { MatButtonModule } from '@angular/material/button';
 
 @NgModule({
     imports: [
@@ -48,11 +48,11 @@ import { MatTabsModule } from '@angular/material/tabs';
         MatGridListModule,
         MatIconModule,
         MatTabsModule,
+        MatButtonModule,
         MatInputModule,
         MatFormFieldModule,
         FormsModule,
         ColorPickerModule,
-        DashboardModule,
         RouterModule.forChild([
             {
                 path: '',
diff --git a/ui/src/app/app-overview/app-overview.module.ts 
b/ui/src/app/app-overview/app-overview.module.ts
index 2b98e9afd6..eef43de838 100644
--- a/ui/src/app/app-overview/app-overview.module.ts
+++ b/ui/src/app/app-overview/app-overview.module.ts
@@ -30,6 +30,7 @@ import { RouterModule } from '@angular/router';
 import { AvailableAppsService } from './apps';
 import { PageName } from '../_enums/page-name.enum';
 import { SharedUiModule } from '@streampipes/shared-ui';
+import { MatButtonModule } from '@angular/material/button';
 
 @NgModule({
     imports: [
@@ -39,13 +40,14 @@ import { SharedUiModule } from '@streampipes/shared-ui';
         FormsModule,
         MatGridListModule,
         MatInputModule,
+        MatButtonModule,
         MatFormFieldModule,
         SharedUiModule,
         RouterModule.forChild([
             {
                 path: '',
                 children: [
-                    { path: '', component: AppOverviewComponent },
+                    { path: 'overview', component: AppOverviewComponent },
                     ...(AvailableAppsService.apps.map(app => {
                         return {
                             path: app.appLink,

Reply via email to