Improving error handling

This commit is contained in:
Martin Stockhammer 2020-12-24 10:37:55 +01:00
parent 7744b086d7
commit cb0e4d19d7
10 changed files with 127 additions and 63 deletions

View File

@ -33,7 +33,6 @@ export class AppNotification {
this.header = header;
this.body = body;
this.timestamp = timestamp;
console.log("Options " + JSON.stringify(options));
if (options.classname) {
this.classname = options.classname;
}
@ -49,7 +48,7 @@ export class AppNotification {
}
public toString(): string {
return this.origin + ',classname:' + this.classname + ", delay:" + this.delay +", context: "+JSON.stringify(this.contextData);
return this.origin + ',classname:' + this.classname + ", delay:" + this.delay ;
}
}

View File

@ -16,16 +16,7 @@
* under the License.
*/
import {
AfterContentInit,
ChangeDetectorRef,
Component,
EventEmitter,
OnInit,
Output,
TemplateRef,
ViewChild
} from '@angular/core';
import {AfterContentInit, Component, EventEmitter, OnInit, Output, ViewChild} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {FormBuilder, Validators} from "@angular/forms";
import {RoleService} from "@app/services/role.service";
@ -42,6 +33,7 @@ import {UserService} from "@app/services/user.service";
import {UserInfo} from '@app/model/user-info';
import {HttpResponse} from "@angular/common/http";
import {PaginatedEntitiesComponent} from "@app/modules/shared/paginated-entities/paginated-entities.component";
import {ToastService} from "@app/services/toast.service";
@Component({
selector: 'app-manage-roles-edit',
@ -73,7 +65,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
roleIdEvent: EventEmitter<string> = new EventEmitter<string>(true);
constructor(private route: ActivatedRoute, public roleService: RoleService, private userService: UserService,
public fb: FormBuilder, private changeDetect : ChangeDetectorRef) {
public fb: FormBuilder, private toastService: ToastService) {
super(fb);
super.init(fb.group({
id: [''],
@ -151,7 +143,9 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
} else {
return this.roleService.getRole(myId).pipe(tap(role => {
this.roleCache.set(role.id, role);
}),catchError(() => of(this.createRole(id))));
}),catchError((error : ErrorResult) => {
this.showError(error, "roles.edit.errors.retrieveFailed")
return of(this.createRole(id)); }));
}
}));
}
@ -192,6 +186,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
this.error = true;
this.success = false;
this.errorResult = err;
this.showError(err, 'roles.edit.errors.updateFailed',{'role_id':role.id})
return [];
})
).subscribe(roleInfo => {
@ -199,6 +194,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
this.success = true;
this.errorResult = null;
this.result = roleInfo;
this.showSuccess('roles.edit.success.updated',{'role_id':role.id})
this.editMode = false;
});
@ -233,6 +229,17 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
return item.user_id;
}
showError(err: ErrorResult, errorKey:string, params:any={}) : void {
let message = err.error_messages.length>0?err.error_messages[0]:''
params['message']=message
this.toastService.showErrorByKey('manage-roles-edit',errorKey,params)
}
showSuccess(successKey:string, params:any={}) : void {
console.log("Success " + successKey + " - " + JSON.stringify(params));
this.toastService.showSuccessByKey('manage-roles-edit',successKey,params)
}
assignUserRole() {
let userId;
if (typeof(this.userSearchModel)=='string') {
@ -248,6 +255,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
this.error = true;
this.success = false;
this.errorResult = err;
this.showError(err, 'roles.edit.errors.assignFailed', {'role_id':this.editRole.id,'user_id':userId})
return [];
})
).subscribe((response : HttpResponse<Role>) => {
@ -256,6 +264,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
this.errorResult = null;
this.result = response.body;
this.roleUserComponent.changePage(1);
this.showSuccess('roles.edit.success.assign',{'role_id':this.editRole.id,'user_id':userId})
this.userSearchModel=''
});
}
@ -269,6 +278,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
this.error = true;
this.success = false;
this.errorResult = err;
this.showError(err, 'roles.edit.errors.unassignFailed',{'role_id':this.editRole.id,'user_id':user_id})
return [];
})
).subscribe((response : HttpResponse<Role>) => {
@ -278,6 +288,7 @@ export class ManageRolesEditComponent extends EditBaseComponent<Role> implements
this.errorResult = null;
this.result = response.body;
this.roleUserComponent.changePage(1);
this.showSuccess('roles.edit.success.unassign',{'role_id':this.editRole.id,'user_id':user_id})
}
);
}

View File

@ -90,9 +90,6 @@
<button class="btn btn-primary" type="submit"
[attr.disabled]="userForm.valid?null:true">{{'users.add.submit'|translate}}</button>
</div>
<div class="form-group col-md-8">
<button class="btn btn-primary" (click)="showMessage()">Show Message</button>
</div>
<ng-template #successTmpl let-userId="user_id">
User <a [routerLink]="['/security','users','edit',userId]">{{userId}}</a> was added to the list.

View File

@ -17,16 +17,16 @@
*/
import {AfterViewInit, Component, EventEmitter, OnInit, Output} from '@angular/core';
import { Role } from '@app/model/role';
import {Role} from '@app/model/role';
import {UserService} from "@app/services/user.service";
import {ActivatedRoute} from "@angular/router";
import {catchError, filter, map, multicast, share, switchMap, tap} from "rxjs/operators";
import {catchError, filter, map, share, switchMap, tap} from "rxjs/operators";
import {RoleTree} from "@app/model/role-tree";
import {RoleService} from "@app/services/role.service";
import {RoleTemplate} from "@app/model/role-template";
import {Observable, of} from "rxjs";
import {Observable} from "rxjs";
import {Util} from "@app/modules/shared/shared.module";
import { RoleResult } from './role-result';
import {RoleResult} from './role-result';
import {fromArray} from "rxjs/internal/observable/fromArray";
import {ErrorResult} from "@app/model/error-result";
import {HttpResponse} from "@angular/common/http";

View File

@ -16,26 +16,13 @@
* under the License.
*/
import {AfterViewInit, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {concat, merge, Observable, of, pipe, Subject} from "rxjs";
import {
concatAll,
debounceTime,
delay,
distinctUntilChanged,
filter,
map,
mergeMap,
pluck,
share,
startWith,
switchMap,
tap
} from "rxjs/operators";
import {EntityService} from "../../../model/entity-service";
import {FieldToggle} from "../../../model/field-toggle";
import {PageQuery} from "@app/modules/shared/model/page-query";
import { LoadingValue } from '../shared.module';
import {AfterViewInit, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {concat, merge, Observable, of, Subject} from "rxjs";
import {debounceTime, distinctUntilChanged, filter, map, pluck, startWith, switchMap} from "rxjs/operators";
import {EntityService} from "@app/model/entity-service";
import {FieldToggle} from "@app/model/field-toggle";
import {PageQuery} from "../model/page-query";
import {LoadingValue} from '../model/loading-value';
import {PagedResult} from "@app/model/paged-result";

View File

@ -41,7 +41,7 @@ import {AppNotification} from "@app/model/app-notification";
<ng-template #text>{{ toast.body }}</ng-template>
</ngb-toast>
`,
styles: [".ngb-toasts{margin:.5em;padding:0.5em;position:fixed;right:2px;top:2px;z-index:1200}"
styles: [".ngb-toasts{margin:.5em;padding:0.5em;position:fixed;right:2px;top:20px;z-index:1200}"
],
host: {'[class.ngb-toasts]': 'true'}
})

View File

@ -16,10 +16,10 @@
* under the License.
*/
import { Pipe, PipeTransform } from '@angular/core';
import {concat, isObservable, of } from 'rxjs';
import {catchError, map, startWith, tap } from 'rxjs/operators';
import {LoadingValue} from "@app/modules/shared/shared.module";
import {Pipe, PipeTransform} from '@angular/core';
import {isObservable, of} from 'rxjs';
import {catchError, map, startWith} from 'rxjs/operators';
import {LoadingValue} from "@app/modules/shared/model/loading-value";
@Pipe({
name: 'withLoading'

View File

@ -19,13 +19,14 @@
import { Injectable } from '@angular/core';
import {ArchivaRequestService} from "@app/services/archiva-request.service";
import {RoleTemplate} from "@app/model/role-template";
import { Observable } from 'rxjs';
import {Observable, throwError} from 'rxjs';
import { Role } from '@app/model/role';
import {HttpResponse} from "@angular/common/http";
import {HttpErrorResponse, HttpResponse} from "@angular/common/http";
import {PagedResult} from "@app/model/paged-result";
import {UserInfo} from "@app/model/user-info";
import {RoleUpdate} from "@app/model/role-update";
import { User } from '@app/model/user';
import {catchError} from "rxjs/operators";
@Injectable({
providedIn: 'root'
@ -35,15 +36,24 @@ export class RoleService {
constructor(private rest: ArchivaRequestService) { }
public getTemplates() : Observable<RoleTemplate[]> {
return this.rest.executeRestCall("get", "redback", "roles/templates", null);
return this.rest.executeRestCall<RoleTemplate[]>("get", "redback", "roles/templates", null).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public assignRole(roleId, userId) : Observable<HttpResponse<Role>> {
return this.rest.executeResponseCall<Role>("put", "redback", "roles/" + roleId + "/user/" + userId, null);
return this.rest.executeResponseCall<Role>("put", "redback", "roles/" + roleId + "/user/" + userId, null).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public unAssignRole(roleId, userId) : Observable<HttpResponse<Role>> {
return this.rest.executeResponseCall<Role>("delete", "redback", "roles/" + roleId + "/user/" + userId, null);
return this.rest.executeResponseCall<Role>("delete", "redback", "roles/" + roleId + "/user/" + userId, null).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public query(searchTerm: string, offset: number = 0, limit: number = 10, orderBy: string[] = ['id'], order: string = 'asc'): Observable<PagedResult<Role>> {
@ -59,7 +69,10 @@ export class RoleService {
'limit': limit,
'orderBy': orderBy,
'order': order
});
}).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public queryAssignedUsers(roleId: string,
@ -77,7 +90,10 @@ export class RoleService {
'limit': limit,
'orderBy': orderBy,
'order': order
});
}).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
/**
@ -108,7 +124,10 @@ export class RoleService {
'limit': limit,
'orderBy': orderBy,
'order': order
});
}).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public queryUnAssignedUsers(roleId: string,
@ -126,16 +145,25 @@ export class RoleService {
'limit': limit,
'orderBy': orderBy,
'order': order
});
}).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public getRole(roleId:string) : Observable<Role> {
return this.rest.executeRestCall("get", "redback", "roles/" + roleId, null);
return this.rest.executeRestCall<Role>("get", "redback", "roles/" + roleId, null).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
public updateRole(role:RoleUpdate) : Observable<Role> {
return this.rest.executeRestCall("patch", "redback", "roles/" + role.id, role);
return this.rest.executeRestCall<Role>("patch", "redback", "roles/" + role.id, role).pipe(
catchError((error: HttpErrorResponse) => {
return throwError(this.rest.getTranslatedErrorResult(error));
}));
}
}

View File

@ -19,6 +19,7 @@
import { Injectable, TemplateRef } from '@angular/core';
import {AppNotification} from "@app/model/app-notification";
import {not} from "rxjs/internal-compatibility";
import {TranslateService} from "@ngx-translate/core";
@Injectable({
providedIn: 'root'
@ -30,9 +31,9 @@ export class ToastService {
toasts:AppNotification[]=[]
toastHistory:AppNotification[]=[]
constructor() { }
constructor(private translator: TranslateService) { }
show(origin:string, textOrTpl: string | TemplateRef<any>, options: any = {}) {
public show(origin:string, textOrTpl: string | TemplateRef<any>, options: any = {}): void {
let notification = new AppNotification(origin, textOrTpl, "", options);
this.toasts.push(notification);
this.toastHistory.push(notification);
@ -45,7 +46,7 @@ export class ToastService {
console.log("Notification " + notification);
}
showStandard(origin:string,textOrTpl:string|TemplateRef<any>, options:any={}) {
public showStandard(origin:string,textOrTpl:string|TemplateRef<any>, options:any={}) : void {
options.classname='bg-primary'
if (!options.delay) {
options.delay=8000
@ -53,7 +54,17 @@ export class ToastService {
this.show(origin,textOrTpl,options)
}
showError(origin:string,textOrTpl:string|TemplateRef<any>, options:any={}) {
public showStandardByKey(origin:string,translationKey:string, params:any={}, options:any={} ) : void {
let message:string;
if (params) {
message = this.translator.instant(translationKey, params);
} else {
message = this.translator.instant(translationKey);
}
this.showStandard(origin, message, options);
}
public showError(origin:string,textOrTpl:string|TemplateRef<any>, options:any={}) : void {
options.classname='bg-warning'
options.type='error'
if (!options.delay) {
@ -62,7 +73,17 @@ export class ToastService {
this.show(origin,textOrTpl,options)
}
showSuccess(origin:string,textOrTpl:string|TemplateRef<any>, options:any={}) {
public showErrorByKey(origin:string,translationKey:string, params:any={}, options:any={} ) : void {
let message:string;
if (params) {
message = this.translator.instant(translationKey, params);
} else {
message = this.translator.instant(translationKey);
}
this.showError(origin, message, options);
}
public showSuccess(origin:string,textOrTpl:string|TemplateRef<any>, options:any={}) : void {
options.classname='bg-info'
options.type='success'
if (!options.delay) {
@ -71,6 +92,17 @@ export class ToastService {
this.show(origin,textOrTpl,options)
}
public showSuccessByKey(origin:string,translationKey:string, params:any={}, options:any={} ) : void {
let message:string;
if (params) {
message = this.translator.instant(translationKey, params);
} else {
message = this.translator.instant(translationKey);
}
this.showSuccess(origin, message, options);
}
remove(toast) {
this.toasts = this.toasts.filter(t => t != toast);
}

View File

@ -143,7 +143,17 @@
"noUsersAssigned": "There are no users assigned to this role",
"assignUserSearch": "Search and assign users to this role",
"userSearchFailed": "Sorry, could not load users",
"assignButton": "Assign"
"assignButton": "Assign",
"errors": {
"updateFailed": "Could not update role {role_id}: {message}",
"assignFailed": "Could not assign role {role_id} to user {user_id}: {message}",
"retrieveFailed": "Could not retrieve role information: {message}"
},
"success": {
"updated": "Role {role_id} was updated",
"assign": "Role {role_id} was assigned to user {user_id}",
"unassign": "Removed assignment of {role_id} to {user_id}"
}
},
"attributes": {
"id": "Identifier",