From 158d8ec3e28259d1e346b95e351c42e8f6ad30c5 Mon Sep 17 00:00:00 2001
From: mechtelm <nicht@mehr.fragen>
Date: Thu, 23 Apr 2020 14:33:07 +0200
Subject: [PATCH] added ApiError instead of number as error result of all
 backend.services; added throwError to cancel loading of units

---
 src/app/app.interceptor.ts                    |  11 +-
 src/app/app.interfaces.ts                     |   9 +
 src/app/backend.service.ts                    |  39 ++--
 src/app/superadmin/backend.service.ts         |  78 +++++---
 src/app/superadmin/users/users.component.ts   |  24 +--
 src/app/test-controller/backend.service.ts    |  71 +++++---
 .../test-controller.component.html            |   8 +-
 .../test-controller.component.ts              | 166 +++++++++---------
 .../test-controller.interfaces.ts             |   9 +
 .../test-controller.service.ts                |  23 +--
 src/app/workspace-admin/backend.service.ts    | 138 +++++++++++----
 .../workspace-admin/files/files.component.ts  |  63 ++-----
 .../iqbFilesUpload.component.ts               |  13 +-
 .../results/results.component.ts              |  46 +----
 .../syscheck/syscheck.component.ts            |  55 ++----
 .../workspace-admin/workspace.interfaces.ts   |   4 +-
 16 files changed, 398 insertions(+), 359 deletions(-)

diff --git a/src/app/app.interceptor.ts b/src/app/app.interceptor.ts
index 8e27a211..fc7b0b78 100644
--- a/src/app/app.interceptor.ts
+++ b/src/app/app.interceptor.ts
@@ -7,6 +7,7 @@ import {
 import {Observable, throwError} from 'rxjs';
 import {catchError, tap} from "rxjs/operators";
 import {Router, RouterState, RouterStateSnapshot} from "@angular/router";
+import {ApiError} from "./app.interfaces";
 
 @Injectable()
 export class AuthInterceptor implements HttpInterceptor {
@@ -21,7 +22,7 @@ export class AuthInterceptor implements HttpInterceptor {
         description: "Keine weiteren Server-Aufrufe erlaubt",
         category: "FATAL"
       });
-      return throwError(500);
+      return throwError(new ApiError(500, "API-Version ungültig"));
     }
 
     // if (request.headers.get('AuthToken') !== null) {
@@ -51,10 +52,11 @@ export class AuthInterceptor implements HttpInterceptor {
       }),
       catchError(e => {
         this.mds.decrementDelayedProcessesCount();
-        let errorCode = 999;
+        let apiError = new ApiError(999);
         if (e instanceof HttpErrorResponse) {
           const httpError = e as HttpErrorResponse;
-          errorCode = httpError.status;
+          apiError.code = httpError.status;
+          apiError.info = httpError.message;
           if (httpError.error instanceof ErrorEvent) {
             this.mds.appError$.next({
               label: 'Fehler in der Netzwerkverbindung',
@@ -68,6 +70,7 @@ export class AuthInterceptor implements HttpInterceptor {
             switch (httpError.status) {
               case 400: {
                 ignoreError = true;
+                // TODO get detailed error message from body
                 break;
               }
               case 401: {
@@ -108,7 +111,7 @@ export class AuthInterceptor implements HttpInterceptor {
           }
         }
 
-        return throwError(errorCode);
+        return throwError(apiError);
       })
     )
   }
diff --git a/src/app/app.interfaces.ts b/src/app/app.interfaces.ts
index 9ca63cd4..9b5e7b45 100644
--- a/src/app/app.interfaces.ts
+++ b/src/app/app.interfaces.ts
@@ -58,6 +58,15 @@ export interface AppError {
   category: 'WARNING' | 'FATAL' | 'PROBLEM'
 }
 
+export class ApiError {
+  code: number;
+  info: string;
+  constructor(code: number, info = '') {
+    this.code = code;
+    this.info = info
+  }
+}
+
 export interface SysCheckInfo {
   workspaceId: string;
   name: string;
diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts
index 6906fbb2..7df9f8b5 100644
--- a/src/app/backend.service.ts
+++ b/src/app/backend.service.ts
@@ -8,7 +8,7 @@ import {
   SysCheckInfo,
   AuthData,
   WorkspaceData,
-  BookletData, MonitorScopeData
+  BookletData, MonitorScopeData, ApiError
 } from './app.interfaces';
 
 // ============================================================================
@@ -28,7 +28,10 @@ export class BackendService {
       return this.http
         .put<AuthData>(this.serverUrl + 'session/admin', {name, password})
         .pipe(
-          catchError(errCode => of(errCode)),
+          catchError((err: ApiError) => {
+            console.warn(`login Api-Error: ${err.code} ${err.info} `);
+            return of(err.code)
+          }),
           switchMap(authData => {
             if (typeof authData === 'number') {
               const errCode = authData as number;
@@ -53,7 +56,10 @@ export class BackendService {
     return this.http
       .put<AuthData>(this.serverUrl + 'session/login', {name})
       .pipe(
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => {
+          console.warn(`nameOnlyLogin Api-Error: ${err.code} ${err.info} `);
+          return of(err.code)
+        })
       );
   }
 
@@ -61,21 +67,24 @@ export class BackendService {
     return this.http
       .put<AuthData>(this.serverUrl + 'session/person', {code})
       .pipe(
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => {
+          console.warn(`codeLogin Api-Error: ${err.code} ${err.info} `);
+          return of(err.code)
+        })
       );
   }
 
   getWorkspaceData(workspaceId: string): Observable<WorkspaceData> {
     return this.http
-          .get<WorkspaceData>(this.serverUrl + 'workspace/' + workspaceId)
-          .pipe(catchError(() => {
-            console.warn('get workspace data failed for ' + workspaceId);
-            return of(<WorkspaceData>{
-              id: workspaceId,
-              name: workspaceId,
-              role: "n.d."
-            })
-          }));
+      .get<WorkspaceData>(this.serverUrl + 'workspace/' + workspaceId)
+      .pipe(catchError(() => {
+        console.warn('get workspace data failed for ' + workspaceId);
+        return of(<WorkspaceData>{
+          id: workspaceId,
+          name: workspaceId,
+          role: "n.d."
+        })
+      }));
   }
 
   getMonitorScopeData(monitorScopeId: string): Observable<MonitorScopeData> {
@@ -95,7 +104,7 @@ export class BackendService {
     return this.http
       .get<AuthData>(this.serverUrl + 'session')
       .pipe(
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => of(err.code))
       )
   }
 
@@ -123,7 +132,7 @@ export class BackendService {
       .put<number>(this.serverUrl + 'test', {bookletName})
       .pipe(
         map((testId: number) => String(testId)),
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => of(err.code))
       );
   }
 
diff --git a/src/app/superadmin/backend.service.ts b/src/app/superadmin/backend.service.ts
index f8737598..d0119fe8 100644
--- a/src/app/superadmin/backend.service.ts
+++ b/src/app/superadmin/backend.service.ts
@@ -3,6 +3,7 @@ import { HttpClient } from '@angular/common/http';
 import { Observable, of } from 'rxjs';
 import { catchError } from 'rxjs/operators';
 import {IdAndName, IdLabelSelectedData, IdRoleData, UserData} from "./superadmin.interfaces";
+import {ApiError} from "../app.interfaces";
 
 
 @Injectable({
@@ -17,93 +18,120 @@ export class BackendService {
   }
 
   getUsers(): Observable<UserData[]> {
-
     return this.http
       .get<UserData[]>(this.serverUrl + 'users')
-      .pipe(catchError(() => []));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`getUsers Api-Error: ${err.code} ${err.info} `);
+        return []
+      }));
   }
 
   addUser(name: string, password: string): Observable<Boolean> {
-
     return this.http
       .put<Boolean>(this.serverUrl + 'user', {n: name, p: password})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`addUser Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   changePassword(userId: number, password: string): Observable<Boolean> {
-
     return this.http
       .patch<Boolean>(this.serverUrl + `user/${userId}/password`, {p: password})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`changePassword Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   setSuperUserStatus(userId: number, changeToSuperUser: boolean, password: string): Observable<Boolean> {
-
     return this.http
       .patch<Boolean>(this.serverUrl + `user/${userId}/super-admin/` + (changeToSuperUser ? 'on' : 'off'), {p: password})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`setSuperUserStatus Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   deleteUsers(users: string[]): Observable<Boolean> {
 
     return this.http
       .request<boolean>('delete', this.serverUrl + 'users', {body: {u: users}})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`deleteUsers Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   getWorkspacesByUser(userId: number): Observable<IdRoleData[]> {
-
     return this.http
       .get<IdLabelSelectedData[]>(this.serverUrl + `user/${userId}/workspaces`)
-      .pipe(catchError(() => []));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`getWorkspacesByUser Api-Error: ${err.code} ${err.info} `);
+        return []
+      }));
   }
 
   setWorkspacesByUser(userId: number, accessTo: IdRoleData[]): Observable<Boolean> {
-
     return this.http
       .patch<Boolean>(this.serverUrl + `user/${userId}/workspaces`, {ws: accessTo})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`setWorkspacesByUser Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   addWorkspace(name: string): Observable<Boolean> {
-
     return this.http
       .put<Boolean>(this.serverUrl + 'workspace', {name: name})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`addWorkspace Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   renameWorkspace(workspaceId: number, wsName: string): Observable<Boolean> {
-
     return this.http
       .patch<Boolean>(this.serverUrl + `workspace/${workspaceId}`, {name: wsName})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`renameWorkspace Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   deleteWorkspaces(workspaces: number[]): Observable<Boolean> {
-
     return this.http
       .request<Boolean>('delete', this.serverUrl + 'workspaces', {body: {ws: workspaces}})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`deleteWorkspaces Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   getUsersByWorkspace(workspaceId: number): Observable<IdRoleData[]> {
-
     return this.http
       .get<IdRoleData[]>(this.serverUrl + `workspace/${workspaceId}/users`)
-      .pipe(catchError(() => []));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`getUsersByWorkspace Api-Error: ${err.code} ${err.info} `);
+        return []
+      }));
   }
 
   setUsersByWorkspace(workspaceId: number, accessing: IdRoleData[]): Observable<Boolean> {
-
     return this.http
       .patch<Boolean>(this.serverUrl + `workspace/${workspaceId}/users`, {u: accessing})
-      .pipe(catchError(() => of(false)));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`setUsersByWorkspace Api-Error: ${err.code} ${err.info} `);
+        return of(false)
+      }));
   }
 
   getWorkspaces(): Observable<IdAndName[]> {
-
     return this.http
       .get<IdAndName[]>(this.serverUrl + 'workspaces')
-      .pipe(catchError(() => []));
+      .pipe(catchError((err: ApiError) => {
+        console.warn(`getWorkspaces Api-Error: ${err.code} ${err.info} `);
+        return []
+      }));
   }
 }
diff --git a/src/app/superadmin/users/users.component.ts b/src/app/superadmin/users/users.component.ts
index 53d59e49..23f897c3 100644
--- a/src/app/superadmin/users/users.component.ts
+++ b/src/app/superadmin/users/users.component.ts
@@ -12,7 +12,7 @@ import { FormGroup } from '@angular/forms';
 import { SelectionModel } from '@angular/cdk/collections';
 import {
   ConfirmDialogComponent, ConfirmDialogData, MessageDialogComponent,
-  MessageDialogData, MessageType, ServerError
+  MessageDialogData, MessageType
 } from 'iqb-components';
 import { MainDataService } from 'src/app/maindata.service';
 import {IdRoleData, UserData} from "../superadmin.interfaces";
@@ -239,15 +239,7 @@ export class UsersComponent implements OnInit {
     this.pendingWorkspaceChanges = false;
     if (this.selectedUser > -1) {
       this.bs.getWorkspacesByUser(this.selectedUser).subscribe(dataresponse => {
-        if (dataresponse instanceof ServerError) {
-          this.mds.appError$.next({
-            label: (dataresponse as ServerError).labelNice,
-            description: (dataresponse as ServerError).labelSystem,
-            category: "PROBLEM"
-          });
-        } else {
-          this.WorkspacelistDatasource = new MatTableDataSource(dataresponse);
-        }
+        this.WorkspacelistDatasource = new MatTableDataSource(dataresponse);
       })
     } else {
       this.WorkspacelistDatasource = null;
@@ -284,16 +276,8 @@ export class UsersComponent implements OnInit {
     this.tableselectionCheckbox.clear();
     this.tableselectionRow.clear();
     this.bs.getUsers().subscribe(dataresponse => {
-      if (dataresponse instanceof ServerError) {
-        this.mds.appError$.next({
-          label: (dataresponse as ServerError).labelNice,
-          description: (dataresponse as ServerError).labelSystem,
-          category: "PROBLEM"
-        });
-      } else {
-        this.objectsDatasource = new MatTableDataSource(dataresponse);
-        this.objectsDatasource.sort = this.sort;
-      }
+      this.objectsDatasource = new MatTableDataSource(dataresponse);
+      this.objectsDatasource.sort = this.sort;
     });
   }
 
diff --git a/src/app/test-controller/backend.service.ts b/src/app/test-controller/backend.service.ts
index eef29267..4140b093 100644
--- a/src/app/test-controller/backend.service.ts
+++ b/src/app/test-controller/backend.service.ts
@@ -3,6 +3,7 @@ import { HttpClient, HttpParams } from '@angular/common/http';
 import { Observable, of } from 'rxjs';
 import { catchError, map } from 'rxjs/operators';
 import {UnitData, TaggedString, TestData} from './test-controller.interfaces';
+import {ApiError} from "../app.interfaces";
 
 
 @Injectable({
@@ -15,46 +16,53 @@ export class BackendService {
     private http: HttpClient
   ) { }
 
-
   saveUnitReview(testId: string, unitName: string, priority: number, categories: string, entry: string)
     : Observable<boolean> {
     return this.http
       .put(this.serverUrl + `test/${testId}/unit/${unitName}/review`, {priority, categories, entry})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`saveUnitReview Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   saveBookletReview(testId: string, priority: number, categories: string, entry: string): Observable<boolean> {
     return this.http
       .put(this.serverUrl + `test/${testId}/review`, {priority, categories, entry})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`saveBookletReview Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
-  getTestData(testId: string): Observable<TestData | number> {
+  getTestData(testId: string): Observable<TestData | boolean> {
     return this.http
       .get<TestData>(this.serverUrl + 'test/' + testId)
       .pipe(
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => {
+          console.warn(`getTestData Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
-  getUnitData(testId: string, unitid: string): Observable<UnitData | number> {
+  getUnitData(testId: string, unitid: string): Observable<UnitData | boolean> {
     return this.http
       .get<UnitData>(this.serverUrl + 'test/' + testId + '/unit/' + unitid)
       .pipe(
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => {
+          console.warn(`getUnitData Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   getResource(testId: string, internalKey: string, resId: string, versionning = false): Observable<TaggedString | number> {
     return this.http
       .get(
@@ -65,68 +73,83 @@ export class BackendService {
         })
       .pipe(
         map(def => <TaggedString>{tag: internalKey, value: def}),
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => {
+          console.warn(`getResource Api-Error: ${err.code} ${err.info} `);
+          return of(err.code)
+        })
       );
   }
 
-
   addUnitLog(testId: string, timestamp: number, unitName: string, entry: string): Observable<boolean> {
     return this.http
       .put(this.serverUrl + `test/${testId}/unit/${unitName}/log`, {timestamp, entry})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`addUnitLog Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   addBookletLog(testId: string, timestamp: number, entry: string): Observable<boolean> {
     return this.http
       .put(this.serverUrl + `test/${testId}/log`, {timestamp, entry})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`addBookletLog Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   setUnitState(testId: string, unitName: string, stateKey: string, state: string): Observable<boolean> {
     return this.http
       .patch(this.serverUrl + `test/${testId}/unit/${unitName}/state`, {key: stateKey, value: state})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`setUnitState Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   setBookletState(testId: string, stateKey: string, state: string): Observable<boolean> {
     return this.http
       .patch(this.serverUrl + `test/${testId}/state`, {key: stateKey, value: state})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`setBookletState Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   newUnitResponse(testId: string, timestamp: number, unitName: string, response: string, responseType: string)
     : Observable<boolean> {
     return this.http
       .put(this.serverUrl + `test/${testId}/unit/${unitName}/response`, {timestamp, response, responseType})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`newUnitResponse Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 
-
   newUnitRestorePoint(testId: string, unitName: string, timestamp: number, restorePoint: string): Observable<boolean> {
     return this.http
       .patch(this.serverUrl + `test/${testId}/unit/${unitName}/restorepoint`, {timestamp, restorePoint})
       .pipe(
         map(() => true),
-        catchError(() => of(false))
+        catchError((err: ApiError) => {
+          console.warn(`newUnitRestorePoint Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
       );
   }
 }
diff --git a/src/app/test-controller/test-controller.component.html b/src/app/test-controller/test-controller.component.html
index 13a4fda1..c9521482 100644
--- a/src/app/test-controller/test-controller.component.html
+++ b/src/app/test-controller/test-controller.component.html
@@ -5,7 +5,7 @@
   <div fxLayout="row" fxLayoutAlign="end center">
     <p class="timer" *ngIf="tcs.mode !== 'hot'">{{ timerValue?.timeLeftString }}</p>
     <button mat-fab [disabled]="!(tcs.unitPrevEnabled$ | async)" *ngIf="tcs.navArrows" color="accent"
-        (click)="tcs.setUnitNavigationRequest('#previous')" matTooltip="Zurück" fxFlex="none">
+            (click)="tcs.setUnitNavigationRequest(unitNavigationTarget.PREVIOUS)" matTooltip="Zurück" fxFlex="none">
       <i class="material-icons">chevron_left</i>
     </button>
     <div *ngIf="tcs.navButtons" fxLayout="row wrap" fxLayoutAlign="start center" fxFlex="0 3 100%">
@@ -18,14 +18,14 @@
       </div>
     </div>
     <button mat-fab [disabled]="!(tcs.unitNextEnabled$ | async)" *ngIf="tcs.navArrows" color="accent"
-        (click)="tcs.setUnitNavigationRequest('#next')" matTooltip="Weiter" fxFlex="none">
+            (click)="tcs.setUnitNavigationRequest(unitNavigationTarget.NEXT)" matTooltip="Weiter" fxFlex="none">
       <i class="material-icons">chevron_right</i>
     </button>
     <button mat-button (click)="showReviewDialog()" *ngIf="tcs.mode === 'run-review'" matTooltip="Kommentar senden" fxFlex="none">
       <mat-icon>rate_review</mat-icon>
     </button>
-    <button mat-button (click)="tcs.setUnitNavigationRequest('#end')"  *ngIf="tcs.mode !== 'hot'"
-          matTooltip="Zur Testheft-Auswahl zurückkehren" fxFlex="none">
+    <button mat-button (click)="tcs.setUnitNavigationRequest(unitNavigationTarget.END)" *ngIf="tcs.mode !== 'hot'"
+            matTooltip="Zur Testheft-Auswahl zurückkehren" fxFlex="none">
       <mat-icon>exit_to_app</mat-icon>
     </button>
   </div>
diff --git a/src/app/test-controller/test-controller.component.ts b/src/app/test-controller/test-controller.component.ts
index 67990f0d..60d2b3e6 100644
--- a/src/app/test-controller/test-controller.component.ts
+++ b/src/app/test-controller/test-controller.component.ts
@@ -14,12 +14,11 @@ import {
   UnitData,
   MaxTimerDataType,
   TaggedString,
-  ReviewDialogData, RunModeKey
+  ReviewDialogData, RunModeKey, UnitNavigationTarget
 } from './test-controller.interfaces';
-import { Subscription, Observable, of, from } from 'rxjs';
-import { switchMap, concatMap } from 'rxjs/operators';
-import { CustomtextService, ServerError } from 'iqb-components';
-import { appconfig } from '../app.config';
+import {Subscription, Observable, of, from, throwError} from 'rxjs';
+import {switchMap, concatMap, map} from 'rxjs/operators';
+import { CustomtextService } from 'iqb-components';
 import {MatDialog} from "@angular/material/dialog";
 import { MatSnackBar } from '@angular/material/snack-bar';
 
@@ -43,6 +42,7 @@ export class TestControllerComponent implements OnInit, OnDestroy {
   private progressValue = 0;
   private loadedUnitCount = 0;
   private unitLoadQueue: TaggedString[] = [];
+  unitNavigationTarget = UnitNavigationTarget;
 
   constructor (
     @Inject('APP_VERSION') public appVersion: string,
@@ -269,35 +269,6 @@ export class TestControllerComponent implements OnInit, OnDestroy {
     return rootTestlet;
   }
 
-  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
-  // private: get player if not already available
-  // ''''''''''''''''''''''''''''''''''''''''''''''''''''
-  private loadPlayerOk(playerId: string): Observable<boolean> {
-    if (this.tcs.hasPlayer(playerId)) {
-      return of(true);
-    } else {
-      // to avoid multiple calls before returning:
-      this.tcs.addPlayer(playerId, '');
-      return this.bs.getResource(this.tcs.testId, '', this.tcs.normaliseId(playerId, 'html'), true)
-          .pipe(
-            switchMap(myData => {
-              if (myData instanceof ServerError) {
-                console.log('## problem getting player "' + playerId + '"');
-                return of(false);
-              } else {
-                const player = myData as TaggedString;
-                if (player.value.length > 0) {
-                  this.tcs.addPlayer(playerId, player.value);
-                  return of(true);
-                } else {
-                  console.log('## size of player "' + playerId + '" = 0');
-                  return of(false);
-                }
-              }
-            }));
-    }
-  }
-
   private incrementProgressValueBy1() {
     this.loadedUnitCount += 1;
     this.progressValue = this.loadedUnitCount * 100 / this.lastUnitSequenceId;
@@ -306,15 +277,13 @@ export class TestControllerComponent implements OnInit, OnDestroy {
   // ''''''''''''''''''''''''''''''''''''''''''''''''''''
   // private: read unitdata
   // ''''''''''''''''''''''''''''''''''''''''''''''''''''
-  private loadUnitOk (myUnit: UnitDef, sequenceId: number): Observable<boolean> {
+  private loadUnitOk (myUnit: UnitDef, sequenceId: number): Observable<number> {
     myUnit.setCanEnter('n', 'Fehler beim Laden');
     return this.bs.getUnitData(this.tcs.testId, myUnit.id)
       .pipe(
         switchMap(myData => {
-          if (myData instanceof ServerError) {
-            const e = myData as ServerError;
-            console.log('error getting unit "' + myUnit.id + '": ' + e.code.toString() + ' - ' + e.labelNice);
-            return of(false);
+          if (myData === false) {
+            return throwError(`error requesting unit ${this.tcs.testId}/${myUnit.id}`);
           } else {
             const myUnitData = myData as UnitData;
             if (myUnitData.restorepoint) {
@@ -346,35 +315,42 @@ export class TestControllerComponent implements OnInit, OnDestroy {
                 }
               }
             } catch (error) {
-              console.log('error parsing xml for unit "' + myUnit.id + '": ' + error.toString());
-              playerId = null;
-              definitionRef = '';
+              return throwError(`error parsing unit def ${this.tcs.testId}/${myUnit.id} (${error.toString()})`);
             }
-            this.incrementProgressValueBy1();
 
             if (playerId) {
               myUnit.playerId = playerId;
+              if (definitionRef.length > 0) {
+                this.unitLoadQueue.push(<TaggedString>{
+                  tag: sequenceId.toString(),
+                  value: definitionRef
+                });
+              }
+              myUnit.setCanEnter('y', '');
 
-              return this.loadPlayerOk(playerId).pipe(
-                switchMap(ok => {
-                  if (ok && definitionRef.length > 0) {
-                    const newUnditDef: TaggedString = {
-                      tag: sequenceId.toString(),
-                      value: definitionRef
-                    };
-                    this.unitLoadQueue.push(newUnditDef);
-                      myUnit.setCanEnter('y', '');
-                      return of(true);
-                  } else {
-                    if (ok) {
-                      myUnit.setCanEnter('y', '');
-                    }
-                    return of(ok);
-                  }
-                }));
+              if (this.tcs.hasPlayer(playerId)) {
+                return of(sequenceId)
+              } else {
+                // to avoid multiple calls before returning:
+                this.tcs.addPlayer(playerId, '');
+                return this.bs.getResource(this.tcs.testId, '', this.tcs.normaliseId(playerId, 'html'), true)
+                  .pipe(
+                    switchMap(myData => {
+                      if (typeof myData === 'number') {
+                        return throwError(`error getting player "${playerId}"`);
+                      } else {
+                        const player = myData as TaggedString;
+                        if (player.value.length > 0) {
+                          this.tcs.addPlayer(playerId, player.value);
+                          return of(sequenceId);
+                        } else {
+                          return throwError(`error getting player "${playerId}" (size = 0)`);
+                        }
+                      }
+                    }));
+              }
             } else {
-              console.log('error getting unit "' + myUnit.id + '": no player');
-              return of(false);
+              return throwError(`player def missing for unit ${this.tcs.testId}/${myUnit.id}`);
             }
           }
         })
@@ -402,7 +378,7 @@ export class TestControllerComponent implements OnInit, OnDestroy {
             this.timerRunning = false;
             this.timerValue = null;
             if (this.tcs.mode !== 'run-review') {
-              this.tcs.setUnitNavigationRequest('#next');
+              this.tcs.setUnitNavigationRequest(UnitNavigationTarget.NEXT);
             }
           } else if (maxTimerData.type === MaxTimerDataType.CANCELLED) {
             this.snackBar.open(this.cts.getCustomText('booklet_msgTimerCancelled'), '', {duration: 3000});
@@ -431,9 +407,14 @@ export class TestControllerComponent implements OnInit, OnDestroy {
         this.tcs.dataLoading = true;
 
         this.bs.getTestData(this.tcs.testId).subscribe(testDataUntyped => {
-          if (typeof testDataUntyped === 'number') {
-            this.mds.addCustomtextsFromDefList(appconfig.customtextsBooklet);
+          if (testDataUntyped === false) {
+            this.mds.appError$.next({
+              label: "Konnte Testinformation nicht laden",
+              description: "TestController.Component: getTestData()",
+              category: "PROBLEM"
+            });
             this.tcs.dataLoading = false;
+            this.tcs.setUnitNavigationRequest(UnitNavigationTarget.ERROR);
           } else {
             const testData = testDataUntyped as TestData;
             this.tcs.mode = testData.mode;
@@ -456,10 +437,11 @@ export class TestControllerComponent implements OnInit, OnDestroy {
             if (this.tcs.rootTestlet === null) {
               this.mds.appError$.next({
                 label: "Problem beim Laden der Testinformation",
-                description: "TestController.Component: this.getBookletFromXml(testData.xml)",
+                description: "TestController.Component: getBookletFromXml(testData.xml)",
                 category: "PROBLEM"
               });
               this.tcs.dataLoading = false;
+              this.tcs.setUnitNavigationRequest(UnitNavigationTarget.ERROR);
             } else {
               this.tcs.maxUnitSequenceId = this.lastUnitSequenceId - 1;
 
@@ -469,25 +451,29 @@ export class TestControllerComponent implements OnInit, OnDestroy {
               for (let i = 1; i < this.tcs.maxUnitSequenceId + 1; i++) {
                 sequArray.push(i);
               }
+
               this.unitLoadQueueSubscription1 = from(sequArray).pipe(
                 concatMap(uSequ => {
                   const ud = this.tcs.rootTestlet.getUnitAt(uSequ);
                   return this.loadUnitOk(ud.unitDef, uSequ);
                 })
-              ).subscribe(ok => {
-                  if (!ok) {
-                    console.log('unit load problem from loadUnitOk');
-                  }
+              ).subscribe(() => {
+                  this.incrementProgressValueBy1();
+                },
+                errorMessage => {
+                  this.mds.appError$.next({
+                    label: "Problem beim Laden der Testinformation",
+                    description: errorMessage,
+                    category: "PROBLEM"
+                  });
+                  this.tcs.dataLoading = false;
+                  this.tcs.setUnitNavigationRequest(UnitNavigationTarget.ERROR);
                 },
-                err => console.error('unit load error from loadUnitOk: ' + err),
                 () => {
-
-                  // =====================
                   this.tcs.rootTestlet.lockUnitsIfTimeLeftNull();
                   this.tcs.updateMinMaxUnitSequenceId(navTarget);
                   this.loadedUnitCount = 0;
 
-                  // =====================
                   this.unitLoadQueueSubscription2 = from(this.unitLoadQueue).pipe(
                     concatMap(queueEntry => {
                       const unitSequ = Number(queueEntry.tag);
@@ -496,24 +482,36 @@ export class TestControllerComponent implements OnInit, OnDestroy {
                       }
                       // avoid to load unit def if not necessary
                       if (unitSequ < this.tcs.minUnitSequenceId) {
-                        return of({tag: unitSequ.toString(), value: ''});
+                        return of(<TaggedString>{tag: unitSequ.toString(), value: ''});
                       } else {
-                        return this.bs.getResource(this.tcs.testId, queueEntry.tag, queueEntry.value);
+                        return this.bs.getResource(this.tcs.testId, queueEntry.tag, queueEntry.value).pipe(
+                          map(response => {
+                            if (typeof response === 'number') {
+                              return throwError(`error loading voud ${this.tcs.testId} / ${queueEntry.tag} / ${queueEntry.value}: status ${response}`)
+                            } else {
+                              return response
+                            }
+                          })
+                        );
                       }
                     })
                   ).subscribe(
-                    def => {
-                      if (def instanceof ServerError) {
-                        console.log('getting unit data failed ' + def.labelNice + '/' + def.labelSystem);
-                      } else {
-                        const udef = def as TaggedString;
-                        this.tcs.addUnitDefinition(Number(udef.tag), udef.value);
-                      }
+                    (def: TaggedString) => {
+                      this.tcs.addUnitDefinition(Number(def.tag), def.value);
+                    },
+                    errorMessage => {
+                      this.mds.appError$.next({
+                        label: "Problem beim Laden der Testinformation",
+                        description: errorMessage,
+                        category: "PROBLEM"
+                      });
+                      this.tcs.dataLoading = false;
+                      this.tcs.setUnitNavigationRequest(UnitNavigationTarget.ERROR);
                     },
-                    err => console.error('unit load error: ' + err),
                     () => { // complete
                       this.tcs.addBookletLog(LogEntryKey.BOOKLETLOADCOMPLETE);
                       this.tcs.bookletLoadComplete = true;
+
                       if (!this.tcs.lazyloading) {
                         this.showProgress = false;
                         this.tcs.dataLoading = false;
diff --git a/src/app/test-controller/test-controller.interfaces.ts b/src/app/test-controller/test-controller.interfaces.ts
index 65c0a5e0..c8f8068e 100644
--- a/src/app/test-controller/test-controller.interfaces.ts
+++ b/src/app/test-controller/test-controller.interfaces.ts
@@ -128,3 +128,12 @@ export enum NoUnitFlag {
 export interface KeyValuePairNumber {
   [K: string]: number;
 }
+
+export enum UnitNavigationTarget {
+  NEXT = "#next",
+  ERROR = "#error",
+  PREVIOUS = "#previous",
+  FIRST = "#first",
+  LAST = "#last",
+  END = "#end"
+}
diff --git a/src/app/test-controller/test-controller.service.ts b/src/app/test-controller/test-controller.service.ts
index 875efd53..f6cd1959 100644
--- a/src/app/test-controller/test-controller.service.ts
+++ b/src/app/test-controller/test-controller.service.ts
@@ -4,7 +4,7 @@ import { Injectable } from '@angular/core';
 import { Testlet, BookletConfig, MaxTimerData } from './test-controller.classes';
 import {
   LastStateKey, LogEntryKey, UnitRestorePointData, UnitResponseData,
-  MaxTimerDataType, UnitNaviButtonData, KeyValuePairNumber, NoUnitFlag
+  MaxTimerDataType, UnitNaviButtonData, KeyValuePairNumber, NoUnitFlag, UnitNavigationTarget
 } from './test-controller.interfaces';
 import { BackendService } from './backend.service';
 import {Router} from "@angular/router";
@@ -305,15 +305,12 @@ export class TestControllerService {
     }
   }
 
-  public setUnitNavigationRequest(navString: string) {
+  public setUnitNavigationRequest(navString: string = UnitNavigationTarget.NEXT) {
     if (!this.rootTestlet) {
-      console.warn(`TestControllerService.setUnitNavigationRequest: Kein Testheft für "${navString}" verfügbar.`);
+      this.router.navigateByUrl(`/t/${this.testId}/nu/${navString}`);
     } else {
-      if (!navString) {
-        navString = '#next';
-      }
       switch (navString) {
-        case '#next':
+        case UnitNavigationTarget.NEXT:
           let startWith = this.currentUnitSequenceId;
           if (startWith < this.minUnitSequenceId) {
             startWith = this.minUnitSequenceId - 1;
@@ -323,19 +320,23 @@ export class TestControllerService {
             this.router.navigateByUrl(`/t/${this.testId}/u/${nextUnitSequenceId}`);
           }
           break;
-        case '#previous':
+        case UnitNavigationTarget.PREVIOUS:
           this.router.navigateByUrl(`/t/${this.testId}/u/${this.currentUnitSequenceId - 1}`);
           break;
-        case '#first':
+        case UnitNavigationTarget.FIRST:
           this.router.navigateByUrl(`/t/${this.testId}/u/${this.minUnitSequenceId}`);
           break;
-        case '#last':
+        case UnitNavigationTarget.LAST:
           this.router.navigateByUrl(`/t/${this.testId}/u/${this.maxUnitSequenceId}`);
           break;
-        case '#end':
+        case UnitNavigationTarget.END:
           // this.mds.endBooklet(); TODO add some old code to end properly
           this.router.navigateByUrl(`/t/${this.testId}/nu/${NoUnitFlag.END}`);
           break;
+        case UnitNavigationTarget.ERROR:
+          // this.mds.endBooklet(); TODO add some old code to end properly
+          this.router.navigateByUrl(`/t/${this.testId}/nu/${NoUnitFlag.ERROR}`);
+          break;
 
         default:
           this.router.navigateByUrl(`/t/${this.testId}/u/${navString}`);
diff --git a/src/app/workspace-admin/backend.service.ts b/src/app/workspace-admin/backend.service.ts
index ee0b1d1f..7a151d31 100644
--- a/src/app/workspace-admin/backend.service.ts
+++ b/src/app/workspace-admin/backend.service.ts
@@ -3,17 +3,15 @@ import { GetFileResponseData, CheckWorkspaceResponseData, SysCheckStatistics,
 import {Injectable, Inject} from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import {Observable, of} from 'rxjs';
-import { catchError } from 'rxjs/operators';
-import { ErrorHandler, ServerError } from 'iqb-components';
+import { catchError, map } from 'rxjs/operators';
 import {WorkspaceDataService} from "./workspacedata.service";
-import {WorkspaceData} from "../app.interfaces";
+import {ApiError, WorkspaceData} from "../app.interfaces";
 
 @Injectable({
   providedIn: 'root'
 })
 export class BackendService {
 
-
   constructor(
     @Inject('SERVER_URL') private readonly serverUrl: string,
     private wds: WorkspaceDataService,
@@ -25,76 +23,123 @@ export class BackendService {
     return this.http
       .get<WorkspaceData>(this.serverUrl + 'workspace/' + workspaceId)
       .pipe(
-        catchError(errCode => of(errCode))
+        catchError((err: ApiError) => {
+          console.warn(`getWorkspaceData Api-Error: ${err.code} ${err.info} `);
+          return of(err.code)
+        })
       );
   }
 
-  getFiles(): Observable<GetFileResponseData[] | ServerError> {
-
+  getFiles(): Observable<GetFileResponseData[]> {
     return this.http
       .get<GetFileResponseData[]>(this.serverUrl + `workspace/${this.wds.wsId}/files`)
-      .pipe(catchError(ErrorHandler.handle));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getFiles Api-Error: ${err.code} ${err.info} `);
+          return []
+        })
+      );
   }
 
-  deleteFiles(filesToDelete: Array<string>): Observable<FileDeletionReport | ServerError> {
-
+  deleteFiles(filesToDelete: Array<string>): Observable<FileDeletionReport> {
     return this.http
       .request<FileDeletionReport>('delete', this.serverUrl + `workspace/${this.wds.wsId}/files`, {body: {f: filesToDelete}})
-      .pipe(catchError(ErrorHandler.handle));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`deleteFiles Api-Error: ${err.code} ${err.info} `);
+          return of(<FileDeletionReport> {
+            deleted: [],
+            not_allowed: [`deleteFiles Api-Error: ${err.code} ${err.info} `],
+            did_not_exist: []
+          })
+        })
+      );
   }
 
-  checkWorkspace(): Observable<CheckWorkspaceResponseData | ServerError> {
-
+  checkWorkspace(): Observable<CheckWorkspaceResponseData> {
     return this.http
       .get<CheckWorkspaceResponseData>(this.serverUrl + `workspace/${this.wds.wsId}/validation`, {})
-      .pipe(catchError(ErrorHandler.handle));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`checkWorkspace Api-Error: ${err.code} ${err.info} `);
+          return of(<CheckWorkspaceResponseData>{
+            errors: [`checkWorkspace Api-Error: ${err.code} ${err.info} `],
+            infos: [],
+            warnings: []
+          })
+        })
+      );
   }
 
   getResultData(): Observable<ResultData[]> {
-
     return this.http
       .get<ResultData[]>(this.serverUrl + `workspace/${this.wds.wsId}/results`, {})
-      .pipe(catchError(() => []));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getResultData Api-Error: ${err.code} ${err.info} `);
+          return []
+        })
+      );
   }
 
   getResponses(groups: string[]): Observable<UnitResponse[]> {
-
     return this.http
       .get<UnitResponse[]>(this.serverUrl + `workspace/${this.wds.wsId}/responses`, {params: {groups: groups.join(',')}})
-      .pipe(catchError(() => []));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getResponses Api-Error: ${err.code} ${err.info} `);
+          return []
+        })
+      );
   }
 
   getLogs(groups: string[]): Observable<LogData[]> {
-
     return this.http
       .get<LogData[]>(this.serverUrl + `workspace/${this.wds.wsId}/logs`, {params: {groups: groups.join(',')}})
-      .pipe(catchError(() => []));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getLogs Api-Error: ${err.code} ${err.info} `);
+          return []
+        })
+      );
   }
 
   getReviews(groups: string[]): Observable<ReviewData[]> {
-
     return this.http
       .get<ReviewData[]>(this.serverUrl + `workspace/${this.wds.wsId}/reviews`, {params: {groups: groups.join(',')}})
-      .pipe(catchError(() => []));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getReviews Api-Error: ${err.code} ${err.info} `);
+          return []
+        })
+      );
   }
 
-  deleteData(groups: string[]): Observable<boolean | ServerError> {
-
+  deleteData(groups: string[]): Observable<boolean> {
     return this.http
-      .request<boolean>('delete', this.serverUrl + `workspace/${this.wds.wsId}/responses`, {body: {groups: groups}})
-      .pipe(catchError(ErrorHandler.handle));
+      .request('delete', this.serverUrl + `workspace/${this.wds.wsId}/responses`, {body: {groups: groups}})
+      .pipe(
+        map(() => true),
+        catchError((err: ApiError) => {
+          console.warn(`deleteData Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
+      );
   }
 
-  getSysCheckReportList(): Observable<SysCheckStatistics[] | ServerError> {
-
+  getSysCheckReportList(): Observable<SysCheckStatistics[]> {
     return this.http
       .get<ReviewData[]>(this.serverUrl + `workspace/${this.wds.wsId}/sys-check/reports/overview`)
-      .pipe(catchError(() => []));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getSysCheckReportList Api-Error: ${err.code} ${err.info} `);
+          return []
+        })
+      );
   }
 
   getSysCheckReport(reports: string[], enclosure: string, columnDelimiter: string, lineEnding: string)
-    : Observable<Blob|ServerError> {
-
+    : Observable<Blob | boolean> {
     return this.http
       .get(this.serverUrl + `workspace/${this.wds.wsId}/sys-check/reports`,
         {
@@ -109,21 +154,38 @@ export class BackendService {
           },
           responseType: 'blob'
         })
-      .pipe(catchError(ErrorHandler.handle));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`getSysCheckReport Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
+      );
   }
 
-  deleteSysCheckReports(checkIds: string[]): Observable <FileDeletionReport|ServerError> {
-
+  deleteSysCheckReports(checkIds: string[]): Observable <FileDeletionReport> {
     return this.http
       .request<FileDeletionReport>('delete', this.serverUrl + `workspace/${this.wds.wsId}/sys-check/reports`, {body: {checkIds: checkIds}})
-      .pipe(catchError(ErrorHandler.handle));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`deleteSysCheckReports Api-Error: ${err.code} ${err.info} `);
+          return of(<FileDeletionReport> {
+            deleted: [],
+            not_allowed: [`deleteSysCheckReports Api-Error: ${err.code} ${err.info} `],
+            did_not_exist: []
+          })
+        })
+      );
   }
 
-  downloadFile(fileType: string, fileName: string): Observable<Blob|ServerError> {
-
+  downloadFile(fileType: string, fileName: string): Observable<Blob | boolean> {
     return this.http
       .get(this.serverUrl + `workspace/${this.wds.wsId}/file/${fileType}/${fileName}`, {responseType: 'blob'})
-      .pipe(catchError(ErrorHandler.handle));
+      .pipe(
+        catchError((err: ApiError) => {
+          console.warn(`downloadFile Api-Error: ${err.code} ${err.info} `);
+          return of(false)
+        })
+      );
   }
 }
 
diff --git a/src/app/workspace-admin/files/files.component.ts b/src/app/workspace-admin/files/files.component.ts
index ea5b15d9..c7f6883c 100644
--- a/src/app/workspace-admin/files/files.component.ts
+++ b/src/app/workspace-admin/files/files.component.ts
@@ -1,8 +1,7 @@
-import { MainDataService } from '../../maindata.service';
 import { WorkspaceDataService } from '../workspacedata.service';
 import { GetFileResponseData, CheckWorkspaceResponseData } from '../workspace.interfaces';
 import { ConfirmDialogComponent, ConfirmDialogData, MessageDialogComponent,
-  MessageDialogData, MessageType, ServerError } from 'iqb-components';
+  MessageDialogData, MessageType } from 'iqb-components';
 import { MatTableDataSource } from '@angular/material/table';
 import { MatSnackBar } from '@angular/material/snack-bar';
 import {BackendService, FileDeletionReport} from '../backend.service';
@@ -34,7 +33,6 @@ export class FilesComponent implements OnInit {
   constructor(
     @Inject('SERVER_URL') private serverUrl: string,
     private bs: BackendService,
-    private mds: MainDataService,
     public wds: WorkspaceDataService,
     public confirmDialog: MatDialog,
     public messageDialog: MatDialog,
@@ -89,24 +87,16 @@ export class FilesComponent implements OnInit {
         dialogRef.afterClosed().subscribe(result => {
           if (result !== false) {
             // =========================================================
-            this.bs.deleteFiles(filesToDelete).subscribe((fileDeletionReport: FileDeletionReport|ServerError) => {
-              if (fileDeletionReport instanceof ServerError) {
-                this.mds.appError$.next({
-                  label: (fileDeletionReport as ServerError).labelNice,
-                  description: (fileDeletionReport as ServerError).labelSystem,
-                  category: "PROBLEM"
-                });
-              } else {
-                const message = [];
-                if (fileDeletionReport.deleted.length > 0) {
-                  message.push(fileDeletionReport.deleted.length + ' Dateien erfolgreich gelöscht.');
-                }
-                if (fileDeletionReport.not_allowed.length > 0) {
-                  message.push(fileDeletionReport.not_allowed.length + ' Dateien konnten nicht gelöscht werden.');
-                }
-                this.snackBar.open(message.join('<br>'), message.length > 1 ? 'Achtung' : '',  {duration: 1000});
-                this.updateFileList();
+            this.bs.deleteFiles(filesToDelete).subscribe((fileDeletionReport: FileDeletionReport) => {
+              const message = [];
+              if (fileDeletionReport.deleted.length > 0) {
+                message.push(fileDeletionReport.deleted.length + ' Dateien erfolgreich gelöscht.');
               }
+              if (fileDeletionReport.not_allowed.length > 0) {
+                message.push(fileDeletionReport.not_allowed.length + ' Dateien konnten nicht gelöscht werden.');
+              }
+              this.snackBar.open(message.join('<br>'), message.length > 1 ? 'Achtung' : '',  {duration: 1000});
+              this.updateFileList();
             });
             // =========================================================
           }
@@ -134,42 +124,25 @@ export class FilesComponent implements OnInit {
       this.serverfiles = new MatTableDataSource([]);
     } else {
       this.bs.getFiles().subscribe(
-        (filedataresponse: GetFileResponseData[]) => {
-          console.log('updateFileList ok');
-          this.serverfiles = new MatTableDataSource(filedataresponse);
+        (fileList: GetFileResponseData[]) => {
+          this.serverfiles = new MatTableDataSource(fileList);
           this.serverfiles.sort = this.sort;
-        }, (err: ServerError) => {
-          console.log('updateFileList err');
-          this.mds.appError$.next({
-            label: err.labelNice,
-            description: err.labelSystem,
-            category: "PROBLEM"
-          });
         }
       );
     }
   }
 
-
   download(element: GetFileResponseData): void {
-
     this.bs.downloadFile(element.type, element.filename)
       .subscribe(
-        (fileData: Blob|ServerError) => {
-          if (fileData instanceof ServerError) {
-            this.mds.appError$.next({
-              label: (fileData as ServerError).labelNice,
-              description: (fileData as ServerError).labelSystem,
-              category: "PROBLEM"
-            });
-          } else {
-            saveAs(fileData, element.filename);
+        (fileData: Blob|boolean) => {
+          if (fileData !== false) {
+            saveAs(fileData as Blob, element.filename);
           }
         }
       );
   }
 
-
   checkWorkspace() {
     this.checkErrors = [];
     this.checkWarnings = [];
@@ -180,12 +153,6 @@ export class FilesComponent implements OnInit {
         this.checkErrors = checkResponse.errors;
         this.checkWarnings = checkResponse.warnings;
         this.checkInfos = checkResponse.infos;
-      }, (err: ServerError) => {
-        this.mds.appError$.next({
-          label: err.labelNice,
-          description: err.labelSystem,
-          category: "PROBLEM"
-        });
       }
     );
   }
diff --git a/src/app/workspace-admin/files/iqb-files/iqbFilesUpload/iqbFilesUpload.component.ts b/src/app/workspace-admin/files/iqb-files/iqbFilesUpload/iqbFilesUpload.component.ts
index 8f8e832c..8cf0436f 100644
--- a/src/app/workspace-admin/files/iqb-files/iqbFilesUpload/iqbFilesUpload.component.ts
+++ b/src/app/workspace-admin/files/iqb-files/iqbFilesUpload/iqbFilesUpload.component.ts
@@ -1,6 +1,6 @@
 import {Component, EventEmitter, Input, OnInit, Output, HostBinding, OnDestroy} from '@angular/core';
 import { HttpClient, HttpEventType, HttpHeaders, HttpParams,
-  HttpErrorResponse, HttpEvent } from '@angular/common/http';
+  HttpEvent } from '@angular/common/http';
 
 
 @Component({
@@ -153,21 +153,12 @@ import { HttpClient, HttpEventType, HttpHeaders, HttpParams,
               this.remove();
             }
           }
-        }, (errorObj: HttpErrorResponse) => {
+        }, () => {
           if (this.fileUploadSubscription) {
             this.fileUploadSubscription.unsubscribe();
           }
 
           this.status = UploadStatus.error;
-          if (errorObj.status === 401) {
-            this.requestResponseText = 'Fehler: Zugriff verweigert - bitte (neu) anmelden!';
-          } else if (errorObj.status === 400) {
-            this.requestResponseText = 'Fehler: ' + errorObj.error;
-          } else if (errorObj.error instanceof ErrorEvent) {
-            this.requestResponseText = 'Fehler: ' + (<ErrorEvent>errorObj.error).message;
-          } else {
-            this.requestResponseText = 'Fehler: ' + errorObj.message;
-          }
         });
       }
     }
diff --git a/src/app/workspace-admin/results/results.component.ts b/src/app/workspace-admin/results/results.component.ts
index 3af6bbc3..c4971014 100644
--- a/src/app/workspace-admin/results/results.component.ts
+++ b/src/app/workspace-admin/results/results.component.ts
@@ -1,6 +1,6 @@
 import { LogData } from '../workspace.interfaces';
 import { WorkspaceDataService } from '../workspacedata.service';
-import {ConfirmDialogComponent, ConfirmDialogData, ServerError} from 'iqb-components';
+import {ConfirmDialogComponent, ConfirmDialogData} from 'iqb-components';
 import { Component, OnInit, ViewChild } from '@angular/core';
 import { BackendService } from '../backend.service';
 import { MatDialog } from '@angular/material/dialog';
@@ -10,7 +10,6 @@ import { MatTableDataSource } from '@angular/material/table';
 import { SelectionModel } from '@angular/cdk/collections';
 import { saveAs } from 'file-saver';
 import { ResultData, UnitResponse, ReviewData } from '../workspace.interfaces';
-import {MainDataService} from "../../maindata.service";
 
 
 @Component({
@@ -29,7 +28,6 @@ export class ResultsComponent implements OnInit {
     private bs: BackendService,
     public wds: WorkspaceDataService,
     private deleteConfirmDialog: MatDialog,
-    private mds: MainDataService,
     public snackBar: MatSnackBar
   ) { }
 
@@ -48,12 +46,6 @@ export class ResultsComponent implements OnInit {
         (resultData: ResultData[]) => {
           this.resultDataSource = new MatTableDataSource<ResultData>(resultData);
           this.resultDataSource.sort = this.sort;
-        }, (err: ServerError) => {
-          this.mds.appError$.next({
-            label: err.labelNice,
-            description: err.labelSystem,
-            category: "PROBLEM"
-          });
         }
       );
     }
@@ -71,7 +63,6 @@ export class ResultsComponent implements OnInit {
         this.resultDataSource.data.forEach(row => this.tableselectionCheckbox.select(row));
   }
 
-  // 444444444444444444444444444444444444444444444444444444444444444444444444444444444444444
   downloadResponsesCSV() {
     if (this.tableselectionCheckbox.selected.length > 0) {
       const selectedGroups: string[] = [];
@@ -128,12 +119,6 @@ export class ResultsComponent implements OnInit {
           this.snackBar.open('Keine Daten verfügbar.', 'Fehler', {duration: 3000});
         }
         this.tableselectionCheckbox.clear();
-      }, (err: ServerError) => {
-          this.mds.appError$.next({
-            label: err.labelNice,
-            description: err.labelSystem,
-            category: "PROBLEM"
-          });
       });
     }
   }
@@ -194,12 +179,6 @@ export class ResultsComponent implements OnInit {
           this.snackBar.open('Keine Daten verfügbar.', 'Fehler', {duration: 3000});
         }
         this.tableselectionCheckbox.clear();
-      }, (err: ServerError) => {
-          this.mds.appError$.next({
-            label: err.labelNice,
-            description: err.labelSystem,
-            category: "PROBLEM"
-          });
       });
     }
   }
@@ -232,12 +211,6 @@ export class ResultsComponent implements OnInit {
           this.snackBar.open('Keine Daten verfügbar.', 'Fehler', {duration: 3000});
         }
         this.tableselectionCheckbox.clear();
-      }, (err: ServerError) => {
-          this.mds.appError$.next({
-            label: err.labelNice,
-            description: err.labelSystem,
-            category: "PROBLEM"
-          });
       });
     }
   }
@@ -269,15 +242,14 @@ export class ResultsComponent implements OnInit {
       dialogRef.afterClosed().subscribe(result => {
         if (result !== false) {
           // =========================================================
-          this.bs.deleteData(selectedGroups).subscribe(() => {
-              this.tableselectionCheckbox.clear();
-              // TODO refresh list!
-          }, (err: ServerError) => {
-            this.mds.appError$.next({
-              label: err.labelNice,
-              description: err.labelSystem,
-              category: "PROBLEM"
-            });
+          this.bs.deleteData(selectedGroups).subscribe((ok: boolean) => {
+            if (ok) {
+              this.snackBar.open('Löschen erfolgreich.', 'Ok.', {duration: 3000});
+            } else {
+              this.snackBar.open('Löschen nicht erfolgreich.', 'Fehler', {duration: 3000});
+            }
+            this.tableselectionCheckbox.clear();
+            this.updateTable()
           });
         }
       });
diff --git a/src/app/workspace-admin/syscheck/syscheck.component.ts b/src/app/workspace-admin/syscheck/syscheck.component.ts
index 503a8505..0a60bd89 100644
--- a/src/app/workspace-admin/syscheck/syscheck.component.ts
+++ b/src/app/workspace-admin/syscheck/syscheck.component.ts
@@ -1,4 +1,4 @@
-import {ConfirmDialogComponent, ConfirmDialogData, ServerError} from 'iqb-components';
+import {ConfirmDialogComponent, ConfirmDialogData} from 'iqb-components';
 import { Component, OnInit, ViewChild } from '@angular/core';
 import { BackendService } from '../backend.service';
 import { MatDialog } from '@angular/material/dialog';
@@ -8,7 +8,6 @@ import { MatTableDataSource } from '@angular/material/table';
 import { SelectionModel } from '@angular/cdk/collections';
 import { saveAs } from 'file-saver';
 import { SysCheckStatistics } from '../workspace.interfaces';
-import {MainDataService} from "../../maindata.service";
 
 
 @Component({
@@ -26,7 +25,6 @@ export class SyscheckComponent implements OnInit {
   constructor(
     private bs: BackendService,
     private deleteConfirmDialog: MatDialog,
-    private mds: MainDataService,
     public snackBar: MatSnackBar
   ) {
   }
@@ -43,12 +41,6 @@ export class SyscheckComponent implements OnInit {
       (resultData: SysCheckStatistics[]) => {
         this.resultDataSource = new MatTableDataSource<SysCheckStatistics>(resultData);
         this.resultDataSource.sort = this.sort;
-      }, (err: ServerError) => {
-        this.mds.appError$.next({
-          label: err.labelNice,
-          description: err.labelSystem,
-          category: "PROBLEM"
-        });
       }
     );
   }
@@ -74,19 +66,18 @@ export class SyscheckComponent implements OnInit {
       });
       // TODO determine OS dependent line ending char and use this
       this.bs.getSysCheckReport(selectedReports, ';', '"', '\n').subscribe(
-      (reportData: Blob) => {
-        if (reportData.size > 0) {
-          saveAs(reportData, 'iqb-testcenter-syscheckreports.csv');
-        } else {
+      (response) => {
+        if (response === false) {
           this.snackBar.open('Keine Daten verfügbar.', 'Fehler', {duration: 3000});
+        } else {
+          const reportData = response as Blob;
+          if (reportData.size > 0) {
+            saveAs(reportData, 'iqb-testcenter-syscheckreports.csv');
+          } else {
+            this.snackBar.open('Keine Daten verfügbar.', 'Fehler', {duration: 3000});
+          }
+          this.tableselectionCheckbox.clear();
         }
-        this.tableselectionCheckbox.clear();
-      }, (err: ServerError) => {
-          this.mds.appError$.next({
-            label: err.labelNice,
-            description: err.labelSystem,
-            category: "PROBLEM"
-          });
       });
     }
   }
@@ -118,23 +109,15 @@ export class SyscheckComponent implements OnInit {
       dialogRef.afterClosed().subscribe(result => {
         if (result !== false) {
           this.bs.deleteSysCheckReports(selectedReports).subscribe((fileDeletionReport) => {
-            if (fileDeletionReport instanceof ServerError) {
-              this.mds.appError$.next({
-                label: (fileDeletionReport as ServerError).labelNice,
-                description: (fileDeletionReport as ServerError).labelSystem,
-                category: "PROBLEM"
-              });
-            } else {
-              const message = [];
-              if (fileDeletionReport.deleted.length > 0) {
-                message.push(fileDeletionReport.deleted.length + ' Dateien erfolgreich gelöscht.');
-              }
-              if (fileDeletionReport.not_allowed.length > 0) {
-                message.push(fileDeletionReport.not_allowed.length + ' Dateien konnten nicht gelöscht werden.');
-              }
-              this.snackBar.open(message.join('<br>'), message.length > 1 ? 'Achtung' : '', {duration: 1000});
-              this.updateTable();
+            const message = [];
+            if (fileDeletionReport.deleted.length > 0) {
+              message.push(fileDeletionReport.deleted.length + ' Berichte erfolgreich gelöscht.');
+            }
+            if (fileDeletionReport.not_allowed.length > 0) {
+              message.push(fileDeletionReport.not_allowed.length + ' Berichte konnten nicht gelöscht werden.');
             }
+            this.snackBar.open(message.join('<br>'), message.length > 1 ? 'Achtung' : '', {duration: 1000});
+            this.updateTable();
           });
         }
       });
diff --git a/src/app/workspace-admin/workspace.interfaces.ts b/src/app/workspace-admin/workspace.interfaces.ts
index 11cc9ece..75df462d 100644
--- a/src/app/workspace-admin/workspace.interfaces.ts
+++ b/src/app/workspace-admin/workspace.interfaces.ts
@@ -15,13 +15,13 @@ export interface GetFileResponseData {
   isChecked: boolean;
 }
 
-export interface CheckWorkspaceResponseData {
+export interface CheckWorkspaceResponseData
+{
   errors: string[];
   infos: string[];
   warnings: string[];
 }
 
-
 export interface GroupResponse {
   name: string;
   testsTotal: number;
-- 
GitLab