You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/09/18 16:40:03 UTC

[incubator-streampipes] 03/03: [hotfix] Improve error handling in data explorer

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/incubator-streampipes.git

commit 90bdcf422323a507ecb3fb412057862935d95bdc
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Sun Sep 18 18:39:50 2022 +0200

    [hotfix] Improve error handling in data explorer
---
 .../apache/streampipes/ps/DataLakeResourceV4.java  |  25 +---
 .../data-explorer-dashboard-widget.component.html  | 139 +++++++++++----------
 .../data-explorer-dashboard-widget.component.scss  |   4 +
 .../data-explorer-dashboard-widget.component.ts    |   6 +
 .../base/base-data-explorer-widget.directive.ts    |  20 ++-
 .../widgets/base/data-explorer-widget-data.ts      |   7 +-
 6 files changed, 108 insertions(+), 93 deletions(-)

diff --git a/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java b/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java
index f825ae622..840e93933 100644
--- a/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java
+++ b/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java
@@ -27,6 +27,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import org.apache.streampipes.dataexplorer.DataLakeManagementV4;
 import org.apache.streampipes.dataexplorer.v4.ProvidedQueryParams;
+import org.apache.streampipes.model.StreamPipesErrorMessage;
 import org.apache.streampipes.model.datalake.DataLakeConfiguration;
 import org.apache.streampipes.model.datalake.DataLakeMeasure;
 import org.apache.streampipes.rest.core.base.impl.AbstractRestResource;
@@ -160,8 +161,8 @@ public class DataLakeResourceV4 extends AbstractRestResource {
             try {
                 SpQueryResult result = this.dataLakeManagement.getData(sanitizedParams);
                 return ok(result);
-            } catch (IllegalArgumentException e) {
-                return badRequest(e.getMessage());
+            } catch (RuntimeException e) {
+                return badRequest(StreamPipesErrorMessage.from(e));
             }
         }
     }
@@ -242,25 +243,7 @@ public class DataLakeResourceV4 extends AbstractRestResource {
     }
 
     private boolean checkProvidedQueryParams(MultivaluedMap<String, String> providedParams) {
-        if (supportedParams.containsAll(providedParams.keySet())) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private boolean columnsEmpty(MultivaluedMap<String, String> providedParams) {
-        if (providedParams.containsKey("columns")) {
-            for (String column : providedParams.get("columns")) {
-                if ("".equals(column)) {
-                    return true;
-                }
-            };
-        } else {
-            return true;
-        }
-
-        return false;
+        return supportedParams.containsAll(providedParams.keySet());
     }
 
     private ProvidedQueryParams populate(String measurementId, MultivaluedMap<String, String> rawParams) {
diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html
index ff7bb1f16..828fe498b 100644
--- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html
+++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.html
@@ -17,75 +17,82 @@
   -->
 
 <div class="h-100">
-    <div class="box"
-         [ngStyle]="{background: configuredWidget.baseAppearanceConfig.backgroundColor,
+  <div class="box"
+       [ngStyle]="{background: configuredWidget.baseAppearanceConfig.backgroundColor,
          color: configuredWidget.baseAppearanceConfig.textColor,
          height: (gridsterItemComponent.height - 13) + 'px',
          border: editMode && currentlyConfiguredWidgetId === configuredWidget._id ? '4px solid var(--color-accent)' : '2px solid ' + configuredWidget.baseAppearanceConfig.backgroundColor}"
-         [attr.data-cy]="'widget-' + configuredWidget.baseAppearanceConfig.widgetTitle">
-        <div class="widget-header h-40" *ngIf="!previewMode">
-            <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center" class="widget-header-text">
-                {{ configuredWidget.baseAppearanceConfig.widgetTitle }}
-            </div>
-            <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
-                <mat-spinner [diameter]="20" color="primary" class="mr-10" *ngIf="timerActive">
-                </mat-spinner>
-                <div class="time-counter"
-                     *ngIf="editMode"
-                     [ngStyle]="{background: configuredWidget.baseAppearanceConfig.textColor, color: configuredWidget.baseAppearanceConfig.backgroundColor}">
-                    {{loadingTime}}s
-                </div>
-                <button mat-button
-                        mat-icon-button
-                        [matMenuTriggerFor]="menu"
-                        aria-label="More options"
-                        matTooltip="More options"
-                        *ngIf="!editMode"
-                        [attr.data-cy]="'more-options-' + configuredWidget.baseAppearanceConfig.widgetTitle">
-                    <mat-icon>more_vert</mat-icon>
-                </button>
-                <mat-menu #menu="matMenu">
-                    <button mat-menu-item
-                            (click)="downloadDataAsFile()">
-                        <mat-icon>get_app</mat-icon>
-                        <span>Download data</span>
-                    </button>
-                    <button mat-menu-item
-                            (click)="startEditMode()"
-                            *ngIf="hasDataExplorerWritePrivileges"
-                            [attr.data-cy]="'start-edit-' + configuredWidget.baseAppearanceConfig.widgetTitle">
-                        <mat-icon>edit</mat-icon>
-                        <span>Edit Widget</span>
-                    </button>
-                </mat-menu>
-                <button mat-button
-                        mat-icon-button
-                        *ngIf="editMode"
-                        (click)="downloadDataAsFile()">
-                    <mat-icon>get_app</mat-icon>
-                </button>
-                <button mat-button
-                        mat-icon-button
-                        [class.mat-raised-button]="currentlyConfiguredWidgetId === configuredWidget._id"
-                        (click)="triggerWidgetEditMode()"
-                        *ngIf="editMode"
-                        [color]="currentlyConfiguredWidgetId === configuredWidget._id ? 'accent' : ''"
-                        matTooltip="Edit widget"
-                        [attr.data-cy]="'edit-' + configuredWidget.baseAppearanceConfig.widgetTitle">
-                    <mat-icon>edit</mat-icon>
-                </button>
-                <button mat-button
-                        mat-icon-button
-                        (click)="removeWidget()"
-                        matTooltip="Delete widget"
-                        *ngIf="editMode && hasDataExplorerDeletePrivileges"
-                        [attr.data-cy]="'remove-' + configuredWidget.baseAppearanceConfig.widgetTitle">
-                    <mat-icon>clear</mat-icon>
-                </button>
-            </div>
-        </div>
-        <div class="widget-content p-0 gridster-item-content ml-0 mr-0 h-100">
-            <ng-template widgetHost class="h-100 p-0"></ng-template>
+       [attr.data-cy]="'widget-' + configuredWidget.baseAppearanceConfig.widgetTitle">
+    <div class="widget-header h-40" *ngIf="!previewMode">
+      <div fxFlex="100" fxLayout="row" fxLayoutAlign="start center" class="widget-header-text">
+        {{ configuredWidget.baseAppearanceConfig.widgetTitle }}
+      </div>
+      <div fxFlex="100" fxLayout="row" fxLayoutAlign="end center">
+        <mat-spinner [diameter]="20" color="primary" class="mr-10" *ngIf="timerActive">
+        </mat-spinner>
+        <div class="time-counter"
+             *ngIf="editMode"
+             [ngStyle]="{background: configuredWidget.baseAppearanceConfig.textColor, color: configuredWidget.baseAppearanceConfig.backgroundColor}">
+          {{loadingTime}}s
         </div>
+        <button mat-button
+                mat-icon-button
+                [matMenuTriggerFor]="menu"
+                aria-label="More options"
+                matTooltip="More options"
+                *ngIf="!editMode"
+                [attr.data-cy]="'more-options-' + configuredWidget.baseAppearanceConfig.widgetTitle">
+          <mat-icon>more_vert</mat-icon>
+        </button>
+        <mat-menu #menu="matMenu">
+          <button mat-menu-item
+                  (click)="downloadDataAsFile()">
+            <mat-icon>get_app</mat-icon>
+            <span>Download data</span>
+          </button>
+          <button mat-menu-item
+                  (click)="startEditMode()"
+                  *ngIf="hasDataExplorerWritePrivileges"
+                  [attr.data-cy]="'start-edit-' + configuredWidget.baseAppearanceConfig.widgetTitle">
+            <mat-icon>edit</mat-icon>
+            <span>Edit Widget</span>
+          </button>
+        </mat-menu>
+        <button mat-button
+                mat-icon-button
+                *ngIf="editMode"
+                (click)="downloadDataAsFile()">
+          <mat-icon>get_app</mat-icon>
+        </button>
+        <button mat-button
+                mat-icon-button
+                [class.mat-raised-button]="currentlyConfiguredWidgetId === configuredWidget._id"
+                (click)="triggerWidgetEditMode()"
+                *ngIf="editMode"
+                [color]="currentlyConfiguredWidgetId === configuredWidget._id ? 'accent' : ''"
+                matTooltip="Edit widget"
+                [attr.data-cy]="'edit-' + configuredWidget.baseAppearanceConfig.widgetTitle">
+          <mat-icon>edit</mat-icon>
+        </button>
+        <button mat-button
+                mat-icon-button
+                (click)="removeWidget()"
+                matTooltip="Delete widget"
+                *ngIf="editMode && hasDataExplorerDeletePrivileges"
+                [attr.data-cy]="'remove-' + configuredWidget.baseAppearanceConfig.widgetTitle">
+          <mat-icon>clear</mat-icon>
+        </button>
+      </div>
+    </div>
+    <div class="widget-content p-0 gridster-item-content ml-0 mr-0 h-100">
+      <ng-template widgetHost class="h-100 p-0"></ng-template>
+      <div fxFlex="100"
+           fxLayout="column"
+           fxLayoutAlign="center center"
+           *ngIf="errorMessage">
+        <sp-exception-message [message]="errorMessage"
+                              [showDetails]="true"></sp-exception-message>
+      </div>
     </div>
+  </div>
 </div>
diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.scss b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.scss
index c7b8cbec6..2128ace62 100644
--- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.scss
+++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.scss
@@ -92,3 +92,7 @@
     text-align: center;
 }
 
+.h-0 {
+    height: 0px;
+}
+
diff --git a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts
index 4b25aa970..16dd074e9 100644
--- a/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts
+++ b/ui/src/app/data-explorer/components/widget/data-explorer-dashboard-widget.component.ts
@@ -45,6 +45,7 @@ import { WidgetTypeService } from '../../services/widget-type.service';
 import { AuthService } from '../../../services/auth.service';
 import { UserPrivilege } from '../../../_enums/user-privilege.enum';
 import { DialogService, PanelType } from '@streampipes/shared-ui';
+import { StreamPipesErrorMessage } from '../../../../../projects/streampipes/platform-services/src/lib/model/gen/streampipes-model';
 
 @Component({
   selector: 'sp-data-explorer-dashboard-widget',
@@ -103,6 +104,8 @@ export class DataExplorerDashboardWidgetComponent implements OnInit, OnDestroy {
   widgetTypeChangedSubscription: Subscription;
   intervalSubscription: Subscription;
 
+  errorMessage: StreamPipesErrorMessage;
+
   @ViewChild(WidgetDirective, {static: true}) widgetHost!: WidgetDirective;
 
   constructor(private dataViewDataExplorerService: DataViewDataExplorerService,
@@ -160,10 +163,12 @@ export class DataExplorerDashboardWidgetComponent implements OnInit, OnDestroy {
     componentRef.instance.gridMode = this.gridMode;
     const removeSub = componentRef.instance.removeWidgetCallback.subscribe(ev => this.removeWidget());
     const timerSub = componentRef.instance.timerCallback.subscribe(ev => this.handleTimer(ev));
+    const errorSub = componentRef.instance.errorCallback.subscribe(ev => this.errorMessage = ev);
 
     componentRef.onDestroy(destroy => {
       removeSub.unsubscribe();
       timerSub.unsubscribe();
+      errorSub.unsubscribe();
     });
   }
 
@@ -212,4 +217,5 @@ export class DataExplorerDashboardWidgetComponent implements OnInit, OnDestroy {
   handleTimer(start: boolean) {
     start ? this.startLoadingTimer() : this.stopLoadingTimer();
   }
+
 }
diff --git a/ui/src/app/data-explorer/components/widgets/base/base-data-explorer-widget.directive.ts b/ui/src/app/data-explorer/components/widgets/base/base-data-explorer-widget.directive.ts
index dbd5e90c0..151b50362 100644
--- a/ui/src/app/data-explorer/components/widgets/base/base-data-explorer-widget.directive.ts
+++ b/ui/src/app/data-explorer/components/widgets/base/base-data-explorer-widget.directive.ts
@@ -27,6 +27,7 @@ import {
   DatalakeRestService,
   DataViewQueryGeneratorService,
   SpQueryResult,
+  StreamPipesErrorMessage,
   TimeSettings
 } from '@streampipes/platform-services';
 import { ResizeService } from '../../../services/resize.service';
@@ -35,7 +36,7 @@ import { Observable, Subject, Subscription, zip } from 'rxjs';
 import { DataExplorerFieldProviderService } from '../../../services/data-explorer-field-provider-service';
 import { BaseWidgetData } from './data-explorer-widget-data';
 import { TimeSelectionService } from '../../../services/time-selection.service';
-import { switchMap } from 'rxjs/operators';
+import { catchError, switchMap } from 'rxjs/operators';
 
 @Directive()
 export abstract class BaseDataExplorerWidgetDirective<T extends DataExplorerWidgetModel> implements BaseWidgetData<T>, OnInit, OnDestroy {
@@ -48,6 +49,9 @@ export abstract class BaseDataExplorerWidgetDirective<T extends DataExplorerWidg
   @Output()
   timerCallback: EventEmitter<boolean> = new EventEmitter();
 
+  @Output()
+  errorCallback: EventEmitter<StreamPipesErrorMessage> = new EventEmitter<StreamPipesErrorMessage>();
+
   @Input() gridsterItem: GridsterItem;
   @Input() gridsterItemComponent: GridsterItemComponent;
   @Input() editMode: boolean;
@@ -96,9 +100,16 @@ export abstract class BaseDataExplorerWidgetDirective<T extends DataExplorerWidg
     const sourceConfigs = this.dataExplorerWidget.dataConfig.sourceConfigs;
     this.fieldProvider = this.fieldService.generateFieldLists(sourceConfigs);
 
-    this.requestQueue$.pipe(switchMap((observables) => {
-      return zip(...observables);
-    })).subscribe(results => {
+    this.requestQueue$
+      .pipe(switchMap((observables) => {
+      this.errorCallback.emit(undefined);
+      return zip(...observables).pipe(catchError((err) => {
+        this.timerCallback.emit(false);
+        this.errorCallback.emit(err.error);
+        return [];
+      }));
+    }))
+      .subscribe(results => {
       results.forEach((result, index) => result.sourceIndex = index);
       this.validateReceivedData(results);
       this.refreshView();
@@ -167,7 +178,6 @@ export abstract class BaseDataExplorerWidgetDirective<T extends DataExplorerWidg
   }
 
   private loadData(includeTooMuchEventsParameter: boolean) {
-
     let observables: Observable<SpQueryResult>[];
     if (includeTooMuchEventsParameter && !this.dataExplorerWidget.dataConfig.ignoreTooMuchDataWarning) {
       observables = this
diff --git a/ui/src/app/data-explorer/components/widgets/base/data-explorer-widget-data.ts b/ui/src/app/data-explorer/components/widgets/base/data-explorer-widget-data.ts
index 4c2d109aa..1a75781ce 100644
--- a/ui/src/app/data-explorer/components/widgets/base/data-explorer-widget-data.ts
+++ b/ui/src/app/data-explorer/components/widgets/base/data-explorer-widget-data.ts
@@ -18,11 +18,16 @@
 
 import { EventEmitter } from '@angular/core';
 import { GridsterItem, GridsterItemComponent } from 'angular-gridster2';
-import { DashboardItem, DataExplorerWidgetModel, TimeSettings } from '@streampipes/platform-services';
+import {
+  DashboardItem,
+  DataExplorerWidgetModel,
+  StreamPipesErrorMessage,
+  TimeSettings } from '@streampipes/platform-services';
 
 export interface BaseWidgetData<T extends DataExplorerWidgetModel> {
   removeWidgetCallback: EventEmitter<boolean>;
   timerCallback: EventEmitter<boolean>;
+  errorCallback: EventEmitter<StreamPipesErrorMessage>;
 
   gridsterItem: GridsterItem;
   gridsterItemComponent: GridsterItemComponent;