mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-18 14:58:15 +00:00
Merge pull request #153 from GNS3/server-errors
Improve errors when server is unavailable, Fixes: #146
This commit is contained in:
@ -40,7 +40,7 @@ import { ProjectService } from './services/project.service';
|
|||||||
import { SymbolService } from "./services/symbol.service";
|
import { SymbolService } from "./services/symbol.service";
|
||||||
import { ServerService } from "./services/server.service";
|
import { ServerService } from "./services/server.service";
|
||||||
import { IndexedDbService } from "./services/indexed-db.service";
|
import { IndexedDbService } from "./services/indexed-db.service";
|
||||||
import { HttpServer } from "./services/http-server.service";
|
import { HttpServer, ServerErrorHandler } from "./services/http-server.service";
|
||||||
import { SnapshotService } from "./services/snapshot.service";
|
import { SnapshotService } from "./services/snapshot.service";
|
||||||
import { ProgressDialogService } from "./common/progress-dialog/progress-dialog.service";
|
import { ProgressDialogService } from "./common/progress-dialog/progress-dialog.service";
|
||||||
import { NodeService } from "./services/node.service";
|
import { NodeService } from "./services/node.service";
|
||||||
@ -79,14 +79,21 @@ import { LocalServerComponent } from './components/local-server/local-server.com
|
|||||||
import { ProgressComponent } from './common/progress/progress.component';
|
import { ProgressComponent } from './common/progress/progress.component';
|
||||||
import { ProgressService } from "./common/progress/progress.service";
|
import { ProgressService } from "./common/progress/progress.service";
|
||||||
import { version } from "./version";
|
import { version } from "./version";
|
||||||
import { ToasterErrorHandler } from "./toaster-error-handler";
|
import { ToasterErrorHandler } from "./common/error-handlers/toaster-error-handler";
|
||||||
|
import { environment } from "../environments/environment";
|
||||||
|
import { RavenState } from "./common/error-handlers/raven-state-communicator";
|
||||||
|
|
||||||
|
|
||||||
Raven
|
if (environment.production) {
|
||||||
.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
Raven
|
||||||
release: version
|
.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||||
})
|
shouldSendCallback: () => {
|
||||||
.install();
|
return RavenState.shouldSend;
|
||||||
|
},
|
||||||
|
release: version
|
||||||
|
})
|
||||||
|
.install();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -166,7 +173,8 @@ Raven
|
|||||||
SymbolsDataSource,
|
SymbolsDataSource,
|
||||||
SelectionManager,
|
SelectionManager,
|
||||||
InRectangleHelper,
|
InRectangleHelper,
|
||||||
DrawingsDataSource
|
DrawingsDataSource,
|
||||||
|
ServerErrorHandler
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
AddServerDialogComponent,
|
AddServerDialogComponent,
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { PersistenceService } from "angular-persistence";
|
import { PersistenceService } from "angular-persistence";
|
||||||
|
|
||||||
import { SettingsService } from "./services/settings.service";
|
import { SettingsService } from "../../services/settings.service";
|
||||||
import { RavenErrorHandler } from "./raven-error-handler";
|
import { RavenErrorHandler } from "./raven-error-handler";
|
||||||
import { environment } from "../environments/environment";
|
import { environment } from "../../../environments/environment";
|
||||||
|
|
||||||
|
|
||||||
describe('RavenErrorHandler', () => {
|
describe('RavenErrorHandler', () => {
|
||||||
@ -31,11 +31,11 @@ describe('RavenErrorHandler', () => {
|
|||||||
it('should handle error', () => {
|
it('should handle error', () => {
|
||||||
settingsService.set('crash_reports', true);
|
settingsService.set('crash_reports', true);
|
||||||
environment.production = true;
|
environment.production = true;
|
||||||
expect(handler.shouldSend()()).toBeTruthy();
|
expect(handler.shouldSend()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not handle when crash reports are disabled', () => {
|
it('should not handle when crash reports are disabled', () => {
|
||||||
settingsService.set('crash_reports', false);
|
settingsService.set('crash_reports', false);
|
||||||
expect(handler.shouldSend()()).toBeFalsy();
|
expect(handler.shouldSend()).toBeFalsy();
|
||||||
});
|
});
|
||||||
});
|
});
|
21
src/app/common/error-handlers/raven-error-handler.ts
Normal file
21
src/app/common/error-handlers/raven-error-handler.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { ErrorHandler, Inject, Injector } from "@angular/core";
|
||||||
|
|
||||||
|
import { SettingsService } from "../../services/settings.service";
|
||||||
|
import { environment } from "../../../environments/environment";
|
||||||
|
import { RavenState } from "./raven-state-communicator";
|
||||||
|
|
||||||
|
|
||||||
|
export class RavenErrorHandler implements ErrorHandler {
|
||||||
|
constructor(@Inject(Injector) protected injector: Injector) {}
|
||||||
|
|
||||||
|
handleError(err: any): void {
|
||||||
|
RavenState.shouldSend = this.shouldSend();
|
||||||
|
|
||||||
|
console.error(err.originalError || err);
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldSend() {
|
||||||
|
const settingsService: SettingsService = this.injector.get(SettingsService);
|
||||||
|
return environment.production && settingsService.get('crash_reports');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
export class RavenStateCommunicator {
|
||||||
|
public shouldSend = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export var RavenState = new RavenStateCommunicator();
|
@ -1,8 +1,10 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { ToasterService } from "./services/toaster.service";
|
import { ToasterService } from "../../services/toaster.service";
|
||||||
import { MockedToasterService } from "./services/toaster.service.spec";
|
import { MockedToasterService } from "../../services/toaster.service.spec";
|
||||||
import { ToasterErrorHandler } from "./toaster-error-handler";
|
import { ToasterErrorHandler } from "./toaster-error-handler";
|
||||||
import { RavenErrorHandler } from "./raven-error-handler";
|
import { RavenErrorHandler } from "./raven-error-handler";
|
||||||
|
import { SettingsService } from "../../services/settings.service";
|
||||||
|
import { MockedSettingsService } from "../../services/settings.service.spec";
|
||||||
|
|
||||||
|
|
||||||
describe('ToasterErrorHandler', () => {
|
describe('ToasterErrorHandler', () => {
|
||||||
@ -13,6 +15,7 @@ describe('ToasterErrorHandler', () => {
|
|||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ToasterService, useClass: MockedToasterService },
|
{ provide: ToasterService, useClass: MockedToasterService },
|
||||||
|
{ provide: SettingsService, useClass: MockedSettingsService },
|
||||||
RavenErrorHandler,
|
RavenErrorHandler,
|
||||||
ToasterErrorHandler,
|
ToasterErrorHandler,
|
||||||
]
|
]
|
@ -1,5 +1,5 @@
|
|||||||
import { RavenErrorHandler } from "./raven-error-handler";
|
import { RavenErrorHandler } from "./raven-error-handler";
|
||||||
import { ToasterService } from "./services/toaster.service";
|
import { ToasterService } from "../../services/toaster.service";
|
||||||
|
|
||||||
|
|
||||||
export class ToasterErrorHandler extends RavenErrorHandler {
|
export class ToasterErrorHandler extends RavenErrorHandler {
|
||||||
@ -8,8 +8,7 @@ export class ToasterErrorHandler extends RavenErrorHandler {
|
|||||||
super.handleError(err);
|
super.handleError(err);
|
||||||
|
|
||||||
const toasterService = this.injector.get(ToasterService);
|
const toasterService = this.injector.get(ToasterService);
|
||||||
const error = err.originalError || err;
|
toasterService.error(err.message);
|
||||||
toasterService.error(error.message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,8 +1,20 @@
|
|||||||
<div class="overlay" *ngIf="visible">
|
<div class="overlay" *ngIf="visible || error">
|
||||||
<div class="loading-spinner">
|
<div class="loading-spinner" *ngIf="visible && !error">
|
||||||
<mat-spinner color="primary">
|
<mat-spinner color="primary">
|
||||||
</mat-spinner>
|
</mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="error-state" *ngIf="error">
|
||||||
|
<div class="error-icon"><mat-icon>error_outline</mat-icon></div>
|
||||||
|
<div>Error occurred: {{ error.message }}</div>
|
||||||
|
<div>
|
||||||
|
<button mat-button (click)="refresh()" matTooltip="Refresh page" >
|
||||||
|
<mat-icon>refresh</mat-icon>
|
||||||
|
</button>
|
||||||
|
<button mat-button routerLink="/" matTooltip="Go to home" >
|
||||||
|
<mat-icon>home</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,9 +10,19 @@
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-spinner {
|
.loading-spinner, .error-state {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-state div {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-icon mat-icon {
|
||||||
|
font-size: 64px;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
@ -1,25 +1,45 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ProgressComponent } from './progress.component';
|
import { ProgressComponent } from './progress.component';
|
||||||
import { MatProgressSpinnerModule } from "@angular/material";
|
import { MatIconModule, MatProgressSpinnerModule } from "@angular/material";
|
||||||
import { ProgressService } from "./progress.service";
|
import { ProgressService } from "./progress.service";
|
||||||
|
import { RouterTestingModule } from "@angular/router/testing";
|
||||||
|
import { Router } from "@angular/router";
|
||||||
|
import { BehaviorSubject } from "rxjs/BehaviorSubject";
|
||||||
|
|
||||||
|
|
||||||
|
class MockedRouter {
|
||||||
|
events: BehaviorSubject<boolean>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.events = new BehaviorSubject(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('ProgressComponent', () => {
|
describe('ProgressComponent', () => {
|
||||||
let component: ProgressComponent;
|
let component: ProgressComponent;
|
||||||
let fixture: ComponentFixture<ProgressComponent>;
|
let fixture: ComponentFixture<ProgressComponent>;
|
||||||
let progressService: ProgressService;
|
let progressService: ProgressService;
|
||||||
|
let router: MockedRouter;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
|
RouterTestingModule,
|
||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
|
MatIconModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
ProgressService,
|
||||||
|
{ provide: Router, useClass: MockedRouter}
|
||||||
],
|
],
|
||||||
providers: [ ProgressService ],
|
|
||||||
declarations: [ ProgressComponent ]
|
declarations: [ ProgressComponent ]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
progressService = TestBed.get(ProgressService);
|
progressService = TestBed.get(ProgressService);
|
||||||
|
router = TestBed.get(Router);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -45,4 +65,21 @@ describe('ProgressComponent', () => {
|
|||||||
expect(component.visible).toEqual(false);
|
expect(component.visible).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it( 'should set error state when error defined', () => {
|
||||||
|
const error = new Error("test");
|
||||||
|
progressService.setError(error);
|
||||||
|
expect(component.error).toEqual(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it( 'should clear error when changes route', () => {
|
||||||
|
const error = new Error("test");
|
||||||
|
component.error = error;
|
||||||
|
|
||||||
|
spyOn(progressService, 'clear');
|
||||||
|
|
||||||
|
router.events.next(true);
|
||||||
|
|
||||||
|
expect(progressService.clear).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,22 +1,49 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { ProgressService } from "./progress.service";
|
import { ProgressService } from "./progress.service";
|
||||||
|
import { Router } from "@angular/router";
|
||||||
|
import { Subscription } from "rxjs/Subscription";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-progress',
|
selector: 'app-progress',
|
||||||
templateUrl: './progress.component.html',
|
templateUrl: './progress.component.html',
|
||||||
styleUrls: ['./progress.component.scss']
|
styleUrls: ['./progress.component.scss']
|
||||||
})
|
})
|
||||||
export class ProgressComponent implements OnInit {
|
export class ProgressComponent implements OnInit, OnDestroy {
|
||||||
visible = false;
|
visible = false;
|
||||||
|
error: Error;
|
||||||
|
routerSubscription: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private progressService: ProgressService
|
private progressService: ProgressService,
|
||||||
|
private router: Router,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.progressService.state.subscribe((state) => {
|
this.progressService.state.subscribe((state) => {
|
||||||
this.visible = state.visible;
|
this.visible = state.visible;
|
||||||
|
|
||||||
|
// only set error state once; ignore next "correct" states
|
||||||
|
if (state.error && !this.error) {
|
||||||
|
this.error = state.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.clear) {
|
||||||
|
this.error = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// when page changes clear error state
|
||||||
|
this.routerSubscription = this.router.events.subscribe(() => {
|
||||||
|
this.progressService.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
// unfortunately we need to use global var
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.routerSubscription.unsubscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,43 @@
|
|||||||
import { TestBed, inject } from '@angular/core/testing';
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ProgressService } from './progress.service';
|
import { ProgressService, State } from './progress.service';
|
||||||
|
|
||||||
|
|
||||||
describe('ProgressService', () => {
|
describe('ProgressService', () => {
|
||||||
|
let progressService: ProgressService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
providers: [ProgressService]
|
providers: [ProgressService]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
progressService = TestBed.get(ProgressService);
|
||||||
|
|
||||||
|
spyOn(progressService.state, 'next');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', inject([ProgressService], (service: ProgressService) => {
|
it('should be created', () => {
|
||||||
expect(service).toBeTruthy();
|
expect(progressService).toBeTruthy();
|
||||||
}));
|
});
|
||||||
|
|
||||||
|
it('should propagate event when activated', () => {
|
||||||
|
progressService.activate();
|
||||||
|
expect(progressService.state.next).toHaveBeenCalledWith(new State(true));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should propagate event when deactivated', () => {
|
||||||
|
progressService.deactivate();
|
||||||
|
expect(progressService.state.next).toHaveBeenCalledWith(new State(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should propagate event on error', () => {
|
||||||
|
const error = new Error();
|
||||||
|
progressService.setError(error);
|
||||||
|
expect(progressService.state.next).toHaveBeenCalledWith(new State(false, error));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear an error', () => {
|
||||||
|
progressService.clear();
|
||||||
|
expect(progressService.state.next).toHaveBeenCalledWith(new State(false, null, true));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -5,9 +5,13 @@ import { BehaviorSubject } from "rxjs/BehaviorSubject";
|
|||||||
|
|
||||||
export class State {
|
export class State {
|
||||||
public visible: boolean;
|
public visible: boolean;
|
||||||
|
public error: Error;
|
||||||
|
public clear: boolean;
|
||||||
|
|
||||||
constructor(visible: boolean) {
|
constructor(visible: boolean, error?: Error, clear: boolean = false) {
|
||||||
this.visible = visible;
|
this.visible = visible;
|
||||||
|
this.error = error;
|
||||||
|
this.clear = clear;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,6 +21,14 @@ export class ProgressService {
|
|||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
public setError(error: Error) {
|
||||||
|
this.state.next(new State(false, error));
|
||||||
|
}
|
||||||
|
|
||||||
|
public clear() {
|
||||||
|
this.state.next(new State(false, null, true));
|
||||||
|
}
|
||||||
|
|
||||||
public activate() {
|
public activate() {
|
||||||
this.state.next(new State(true));
|
this.state.next(new State(true));
|
||||||
}
|
}
|
||||||
|
@ -62,9 +62,6 @@
|
|||||||
<app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface>
|
<app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="loading-spinner" *ngIf="isLoading">
|
<app-progress></app-progress>
|
||||||
<mat-spinner color="primary">
|
|
||||||
</mat-spinner>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server" [selectionManager]="selectionManager"></app-project-map-shortcuts>
|
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server" [selectionManager]="selectionManager"></app-project-map-shortcuts>
|
||||||
|
@ -41,6 +41,7 @@ import { InRectangleHelper } from "../../cartography/components/map/helpers/in-r
|
|||||||
import { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource";
|
import { DrawingsDataSource } from "../../cartography/datasources/drawings-datasource";
|
||||||
import { Subscription } from "rxjs/Subscription";
|
import { Subscription } from "rxjs/Subscription";
|
||||||
import { SettingsService } from "../../services/settings.service";
|
import { SettingsService } from "../../services/settings.service";
|
||||||
|
import { ProgressService } from "../../common/progress/progress.service";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -65,8 +66,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
protected selectionManager: SelectionManager;
|
protected selectionManager: SelectionManager;
|
||||||
|
|
||||||
public isLoading = true;
|
|
||||||
|
|
||||||
@ViewChild(MapComponent) mapChild: MapComponent;
|
@ViewChild(MapComponent) mapChild: MapComponent;
|
||||||
|
|
||||||
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
||||||
@ -84,6 +83,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private linkService: LinkService,
|
private linkService: LinkService,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private progressDialogService: ProgressDialogService,
|
private progressDialogService: ProgressDialogService,
|
||||||
|
private progressService: ProgressService,
|
||||||
private toaster: ToasterService,
|
private toaster: ToasterService,
|
||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private settingsService: SettingsService,
|
private settingsService: SettingsService,
|
||||||
@ -98,6 +98,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.progressService.activate();
|
||||||
|
|
||||||
const routeSub = this.route.paramMap.subscribe((paramMap: ParamMap) => {
|
const routeSub = this.route.paramMap.subscribe((paramMap: ParamMap) => {
|
||||||
const server_id = parseInt(paramMap.get('server_id'), 10);
|
const server_id = parseInt(paramMap.get('server_id'), 10);
|
||||||
@ -123,6 +124,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
})
|
})
|
||||||
.subscribe((project: Project) => {
|
.subscribe((project: Project) => {
|
||||||
this.onProjectLoad(project);
|
this.onProjectLoad(project);
|
||||||
|
}, (error) => {
|
||||||
|
this.progressService.setError(error);
|
||||||
|
}, () => {
|
||||||
|
this.progressService.deactivate();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -183,7 +188,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.setUpMapCallbacks(project);
|
this.setUpMapCallbacks(project);
|
||||||
this.setUpWS(project);
|
this.setUpWS(project);
|
||||||
this.isLoading = false;
|
|
||||||
|
this.progressService.deactivate();
|
||||||
});
|
});
|
||||||
this.subscriptions.push(subscription);
|
this.subscriptions.push(subscription);
|
||||||
}
|
}
|
||||||
@ -335,7 +341,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
this.nodesDataSource.clear();
|
this.nodesDataSource.clear();
|
||||||
this.linksDataSource.clear();
|
this.linksDataSource.clear();
|
||||||
|
|
||||||
this.ws.unsubscribe();
|
if (this.ws) {
|
||||||
|
this.ws.unsubscribe();
|
||||||
|
}
|
||||||
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
|
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +62,8 @@ export class ProjectsComponent implements OnInit {
|
|||||||
.list(this.server)
|
.list(this.server)
|
||||||
.subscribe((projects: Project[]) => {
|
.subscribe((projects: Project[]) => {
|
||||||
this.projectDatabase.addProjects(projects);
|
this.projectDatabase.addProjects(projects);
|
||||||
|
}, (error) => {
|
||||||
|
this.progressService.setError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
import * as Raven from 'raven-js';
|
|
||||||
|
|
||||||
import { ErrorHandler, Inject, Injector } from "@angular/core";
|
|
||||||
|
|
||||||
import { SettingsService } from "./services/settings.service";
|
|
||||||
import { environment } from "../environments/environment";
|
|
||||||
|
|
||||||
|
|
||||||
export class RavenErrorHandler implements ErrorHandler {
|
|
||||||
constructor(@Inject(Injector) protected injector: Injector) {}
|
|
||||||
|
|
||||||
handleError(err: any): void {
|
|
||||||
Raven.setShouldSendCallback(this.shouldSend());
|
|
||||||
|
|
||||||
console.error(err.originalError || err);
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldSend() {
|
|
||||||
return () => {
|
|
||||||
const settingsService: SettingsService = this.injector.get(SettingsService);
|
|
||||||
return environment.production && settingsService.get('crash_reports');
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,7 @@ import { HttpClient } from "@angular/common/http";
|
|||||||
import { ApplianceService } from './appliance.service';
|
import { ApplianceService } from './appliance.service';
|
||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { HttpServer } from './http-server.service';
|
import { HttpServer } from './http-server.service';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +17,8 @@ describe('ApplianceService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ApplianceService,
|
ApplianceService,
|
||||||
|
@ -5,6 +5,7 @@ import { HttpClient } from "@angular/common/http";
|
|||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { HttpServer } from './http-server.service';
|
import { HttpServer } from './http-server.service';
|
||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
class MyType {
|
class MyType {
|
||||||
id: number;
|
id: number;
|
||||||
@ -20,7 +21,8 @@ describe('HttpServer', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer
|
HttpServer
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import {HttpHeaders, HttpClient, HttpParams} from '@angular/common/http';
|
import { HttpHeaders, HttpClient, HttpParams, HttpErrorResponse } from '@angular/common/http';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import {Server} from "../models/server";
|
import {Server} from "../models/server";
|
||||||
|
import { catchError } from "rxjs/operators";
|
||||||
|
|
||||||
|
import 'rxjs/add/observable/throw'
|
||||||
|
|
||||||
|
|
||||||
/* tslint:disable:interface-over-type-literal */
|
/* tslint:disable:interface-over-type-literal */
|
||||||
@ -40,57 +43,104 @@ export type HeadersOptions = {
|
|||||||
/* tslint:enable:interface-over-type-literal */
|
/* tslint:enable:interface-over-type-literal */
|
||||||
|
|
||||||
|
|
||||||
|
export class ServerError extends Error {
|
||||||
|
public originalError: Error;
|
||||||
|
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromError(message: string, originalError: Error) {
|
||||||
|
const serverError = new ServerError(message);
|
||||||
|
serverError.originalError = originalError;
|
||||||
|
return serverError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ServerErrorHandler {
|
||||||
|
handleError(error: HttpErrorResponse) {
|
||||||
|
let err: Error = error;
|
||||||
|
|
||||||
|
if (error.name === 'HttpErrorResponse' && error.status === 0) {
|
||||||
|
err = ServerError.fromError("Server is unreachable", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Observable.throw(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HttpServer {
|
export class HttpServer {
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
private errorHandler: ServerErrorHandler
|
||||||
|
) { }
|
||||||
|
|
||||||
get<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
get<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer<JsonOptions>(server, url, options);
|
const intercepted = this.getOptionsForServer<JsonOptions>(server, url, options);
|
||||||
return this.http.get<T>(intercepted.url, intercepted.options as JsonOptions);
|
return this.http
|
||||||
|
.get<T>(intercepted.url, intercepted.options as JsonOptions)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
getText(server: Server, url: string, options?: TextOptions): Observable<string> {
|
getText(server: Server, url: string, options?: TextOptions): Observable<string> {
|
||||||
options = this.getTextOptions(options);
|
options = this.getTextOptions(options);
|
||||||
const intercepted = this.getOptionsForServer<TextOptions>(server, url, options);
|
const intercepted = this.getOptionsForServer<TextOptions>(server, url, options);
|
||||||
return this.http.get(intercepted.url, intercepted.options as TextOptions);
|
return this.http
|
||||||
|
.get(intercepted.url, intercepted.options as TextOptions)
|
||||||
|
.pipe(catchError(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
post<T>(server: Server, url: string, body: any | null, options?: JsonOptions): Observable<T> {
|
post<T>(server: Server, url: string, body: any | null, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer(server, url, options);
|
const intercepted = this.getOptionsForServer(server, url, options);
|
||||||
return this.http.post<T>(intercepted.url, body, intercepted.options);
|
return this.http
|
||||||
|
.post<T>(intercepted.url, body, intercepted.options)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
put<T>(server: Server, url: string, body: any, options?: JsonOptions): Observable<T> {
|
put<T>(server: Server, url: string, body: any, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer(server, url, options);
|
const intercepted = this.getOptionsForServer(server, url, options);
|
||||||
return this.http.put<T>(intercepted.url, body, intercepted.options);
|
return this.http
|
||||||
|
.put<T>(intercepted.url, body, intercepted.options)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
delete<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer(server, url, options);
|
const intercepted = this.getOptionsForServer(server, url, options);
|
||||||
return this.http.delete<T>(intercepted.url, intercepted.options);
|
return this.http
|
||||||
|
.delete<T>(intercepted.url, intercepted.options)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
patch<T>(server: Server, url: string, body: any, options?: JsonOptions): Observable<T> {
|
patch<T>(server: Server, url: string, body: any, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer(server, url, options);
|
const intercepted = this.getOptionsForServer(server, url, options);
|
||||||
return this.http.patch<T>(intercepted.url, body, intercepted.options);
|
return this.http
|
||||||
|
.patch<T>(intercepted.url, body, intercepted.options)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
head<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
head<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer(server, url, options);
|
const intercepted = this.getOptionsForServer(server, url, options);
|
||||||
return this.http.head<T>(intercepted.url, intercepted.options);
|
return this.http
|
||||||
|
.head<T>(intercepted.url, intercepted.options)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
options<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
options<T>(server: Server, url: string, options?: JsonOptions): Observable<T> {
|
||||||
options = this.getJsonOptions(options);
|
options = this.getJsonOptions(options);
|
||||||
const intercepted = this.getOptionsForServer(server, url, options);
|
const intercepted = this.getOptionsForServer(server, url, options);
|
||||||
return this.http.options<T>(intercepted.url, intercepted.options);
|
return this.http
|
||||||
|
.options<T>(intercepted.url, intercepted.options)
|
||||||
|
.pipe(catchError<T, any>(this.errorHandler.handleError));
|
||||||
}
|
}
|
||||||
|
|
||||||
private getJsonOptions(options: JsonOptions): JsonOptions {
|
private getJsonOptions(options: JsonOptions): JsonOptions {
|
||||||
|
@ -8,6 +8,7 @@ import { Server } from '../models/server';
|
|||||||
import { Node } from '../cartography/models/node';
|
import { Node } from '../cartography/models/node';
|
||||||
import { Port } from '../models/port';
|
import { Port } from '../models/port';
|
||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
describe('LinkService', () => {
|
describe('LinkService', () => {
|
||||||
let httpClient: HttpClient;
|
let httpClient: HttpClient;
|
||||||
@ -19,7 +20,8 @@ describe('LinkService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer,
|
HttpServer,
|
||||||
|
@ -5,11 +5,11 @@ import { HttpTestingController, HttpClientTestingModule } from '@angular/common/
|
|||||||
import { HttpServer } from './http-server.service';
|
import { HttpServer } from './http-server.service';
|
||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { Node } from '../cartography/models/node';
|
import { Node } from '../cartography/models/node';
|
||||||
import { Port } from '../models/port';
|
|
||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
import { NodeService } from './node.service';
|
import { NodeService } from './node.service';
|
||||||
import { Appliance } from '../models/appliance';
|
import { Appliance } from '../models/appliance';
|
||||||
import { Project } from '../models/project';
|
import { Project } from '../models/project';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
describe('NodeService', () => {
|
describe('NodeService', () => {
|
||||||
let httpClient: HttpClient;
|
let httpClient: HttpClient;
|
||||||
@ -21,7 +21,8 @@ describe('NodeService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer,
|
HttpServer,
|
||||||
|
@ -10,6 +10,7 @@ import { SettingsService } from "./settings.service";
|
|||||||
import { MockedSettingsService } from "./settings.service.spec";
|
import { MockedSettingsService } from "./settings.service.spec";
|
||||||
import { Observable } from "rxjs/Observable";
|
import { Observable } from "rxjs/Observable";
|
||||||
import { Project } from "../models/project";
|
import { Project } from "../models/project";
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +48,8 @@ describe('ProjectService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer,
|
HttpServer,
|
||||||
|
@ -4,11 +4,10 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
|
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
import { HttpServer } from './http-server.service';
|
import { HttpServer } from './http-server.service';
|
||||||
import { Server } from '../models/server';
|
import { Server } from '../models/server';
|
||||||
import { Node } from '../cartography/models/node';
|
|
||||||
import { Port } from '../models/port';
|
|
||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
import { SnapshotService } from './snapshot.service';
|
import { SnapshotService } from './snapshot.service';
|
||||||
import { Snapshot } from '../models/snapshot';
|
import { Snapshot } from '../models/snapshot';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
describe('SnapshotService', () => {
|
describe('SnapshotService', () => {
|
||||||
@ -21,7 +20,8 @@ describe('SnapshotService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer,
|
HttpServer,
|
||||||
|
@ -7,6 +7,7 @@ import { Server } from '../models/server';
|
|||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
import { SymbolService } from './symbol.service';
|
import { SymbolService } from './symbol.service';
|
||||||
import { Symbol } from '../cartography/models/symbol';
|
import { Symbol } from '../cartography/models/symbol';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
describe('SymbolService', () => {
|
describe('SymbolService', () => {
|
||||||
@ -19,7 +20,8 @@ describe('SymbolService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer,
|
HttpServer,
|
||||||
|
@ -8,6 +8,7 @@ import { Node } from '../cartography/models/node';
|
|||||||
import { Port } from '../models/port';
|
import { Port } from '../models/port';
|
||||||
import { getTestServer } from './testing';
|
import { getTestServer } from './testing';
|
||||||
import { VersionService } from './version.service';
|
import { VersionService } from './version.service';
|
||||||
|
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
|
||||||
|
|
||||||
|
|
||||||
describe('VersionService', () => {
|
describe('VersionService', () => {
|
||||||
@ -20,7 +21,8 @@ describe('VersionService', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
HttpClientTestingModule
|
HttpClientTestingModule,
|
||||||
|
AppTestingModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
HttpServer,
|
HttpServer,
|
||||||
|
14
src/app/testing/app-testing/app-testing.module.ts
Normal file
14
src/app/testing/app-testing/app-testing.module.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ServerErrorHandler } from "../../services/http-server.service";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule
|
||||||
|
],
|
||||||
|
declarations: [],
|
||||||
|
providers: [
|
||||||
|
ServerErrorHandler
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AppTestingModule { }
|
@ -6526,8 +6526,8 @@ range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
|
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
|
||||||
|
|
||||||
raven-js@^3.24.1:
|
raven-js@^3.24.1:
|
||||||
version "3.25.1"
|
version "3.26.3"
|
||||||
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.25.1.tgz#05df39b41af140e3b2fa34e2c522e2b4f99a98be"
|
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.26.3.tgz#0efb49969b5b11ab965f7b0d6da4ca102b763cb0"
|
||||||
|
|
||||||
raven@^2.6.0:
|
raven@^2.6.0:
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
|
Reference in New Issue
Block a user