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