Merge with develop

This commit is contained in:
ziajka
2018-10-15 14:05:55 +02:00
22 changed files with 493 additions and 180 deletions

View File

@ -82,6 +82,8 @@ import { version } from "./version";
import { ToasterErrorHandler } from "./common/error-handlers/toaster-error-handler";
import { environment } from "../environments/environment";
import { RavenState } from "./common/error-handlers/raven-state-communicator";
import { ServerDiscoveryComponent } from "./components/servers/server-discovery/server-discovery.component";
import { ServerDatabase } from './services/server.database';
if (environment.production) {
@ -118,6 +120,7 @@ if (environment.production) {
SettingsComponent,
LocalServerComponent,
ProgressComponent,
ServerDiscoveryComponent,
],
imports: [
NgbModule.forRoot(),
@ -174,7 +177,8 @@ if (environment.production) {
SelectionManager,
InRectangleHelper,
DrawingsDataSource,
ServerErrorHandler
ServerErrorHandler,
ServerDatabase
],
entryComponents: [
AddServerDialogComponent,

View File

@ -2,7 +2,7 @@ import {
Component, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, SimpleChange
} from '@angular/core';
import { D3, D3Service } from 'd3-ng2-service';
import {select, Selection} from 'd3-selection';
import { select, Selection } from 'd3-selection';
import { Node } from "../../models/node";
import { Link } from "../../../models/link";
@ -10,7 +10,7 @@ import { GraphLayout } from "../../widgets/graph-layout";
import { Context } from "../../models/context";
import { Size } from "../../models/size";
import { Drawing } from "../../models/drawing";
import {Symbol} from "../../../models/symbol";
import { Symbol } from "../../models/symbol";
@Component({
@ -30,10 +30,9 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
private d3: D3;
private parentNativeElement: any;
private svg: Selection<SVGSVGElement, any, null, undefined>;
public graphLayout: GraphLayout;
private graphContext: Context;
public graphLayout: GraphLayout;
constructor(protected element: ElementRef,
protected d3Service: D3Service
@ -118,12 +117,6 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy {
this.graphContext.size = this.getSize();
}
if (this.graphContext != null) {
this.svg
.attr('width', this.graphContext.size.width)
.attr('height', this.graphContext.size.height);
}
this.graphLayout.setNodes(this.nodes);
this.graphLayout.setLinks(this.links);
this.graphLayout.setDrawings(this.drawings);

View File

@ -82,6 +82,10 @@ export class GraphLayout implements Widget {
}
draw(view: SVGSelection, context: Context) {
view
.attr('width', context.size.width)
.attr('height', context.size.height);
const canvas = view
.selectAll<SVGGElement, Context>('g.canvas')
.data([context]);

View File

@ -34,18 +34,15 @@ describe('LocalServerComponent', () => {
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LocalServerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
}));
it('should create and redirect to server', fakeAsync(() => {
expect(component).toBeTruthy();
expect(serverService.getLocalServer).toHaveBeenCalled();
// @FIXME: somehow shows it's never called
// expect(router.navigate).toHaveBeenCalledWith('/server', 99, 'projects');
tick();
expect(router.navigate).toHaveBeenCalledWith(['/server', 99, 'projects']);
}));
});

View File

@ -0,0 +1,9 @@
<mat-card class="info" *ngIf="discoveredServer">
<mat-card-content align="center">
We've discovered GNS3 server on <b>{{ discoveredServer.ip }}:{{ discoveredServer.port }}</b>, would you like to add to the list?
</mat-card-content>
<mat-card-actions align="right">
<button mat-button color="accent" (click)="ignore(discoveredServer)">NO</button>
<button mat-button (click)="accept(discoveredServer)">YES</button>
</mat-card-actions>
</mat-card>

View File

@ -0,0 +1,173 @@
import {async, ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing';
import { MatCardModule } from "@angular/material";
import { Observable } from "rxjs/Rx";
import { ServerDiscoveryComponent } from './server-discovery.component';
import { VersionService } from "../../../services/version.service";
import { MockedVersionService } from "../../../services/version.service.spec";
import { Version } from "../../../models/version";
import { Server } from "../../../models/server";
import { ServerService } from '../../../services/server.service';
import { MockedServerService } from '../../../services/server.service.spec';
import { ServerDatabase } from '../../../services/server.database';
describe('ServerDiscoveryComponent', () => {
let component: ServerDiscoveryComponent;
let fixture: ComponentFixture<ServerDiscoveryComponent>;
let mockedVersionService: MockedVersionService;
let mockedServerService: MockedServerService;
beforeEach(async(() => {
mockedServerService = new MockedServerService();
mockedVersionService = new MockedVersionService();
TestBed.configureTestingModule({
imports: [ MatCardModule ],
providers: [
{ provide: VersionService, useFactory: () => mockedVersionService },
{ provide: ServerService, useFactory: () => mockedServerService },
ServerDatabase
],
declarations: [ ServerDiscoveryComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ServerDiscoveryComponent);
component = fixture.componentInstance;
// we don't really want to run it during testing
spyOn(component, 'ngOnInit').and.returnValue(null);
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('isAvailable', () => {
it('should return server object when server is available', () => {
const version = new Version();
version.version = "2.1.8";
const getVersionSpy = spyOn(mockedVersionService, 'get')
.and.returnValue(Observable.of(version));
component.isServerAvailable('127.0.0.1', 3080).subscribe((s) => {
expect(s.ip).toEqual('127.0.0.1');
expect(s.port).toEqual(3080);
});
const server = new Server();
server.ip = '127.0.0.1';
server.port = 3080;
expect(getVersionSpy).toHaveBeenCalledWith(server);
});
it('should throw error once server is not available', () => {
const server = new Server();
server.ip = '127.0.0.1';
server.port = 3080;
const getVersionSpy = spyOn(mockedVersionService, 'get')
.and.returnValue(Observable.throwError(new Error("server is unavailable")));
let hasExecuted = false;
component.isServerAvailable('127.0.0.1', 3080).subscribe((ver) => {}, (err) => {
hasExecuted = true;
expect(err.toString()).toEqual('Error: server is unavailable');
});
expect(getVersionSpy).toHaveBeenCalledWith(server);
expect(hasExecuted).toBeTruthy();
});
});
describe("discovery", () => {
it('should discovery all servers available', (done) => {
const version = new Version();
version.version = "2.1.8";
spyOn(component, 'isServerAvailable').and.callFake((ip, port) => {
const server = new Server();
server.ip = ip;
server.port = port;
return Observable.of(server);
});
component.discovery().subscribe((discovered) => {
expect(discovered[0].ip).toEqual('127.0.0.1');
expect(discovered[0].port).toEqual(3080);
expect(discovered.length).toEqual(1);
done();
});
});
});
describe("discoverFirstAvailableServer", () => {
let server: Server;
beforeEach(function() {
server = new Server();
server.ip = '199.111.111.1',
server.port = 3333;
spyOn(component, 'discovery').and.callFake(() => {
return Observable.of([server]);
});
});
it('should get first server from discovered and with no added before', fakeAsync(() => {
expect(component.discoveredServer).toBeUndefined();
component.discoverFirstAvailableServer();
tick();
expect(component.discoveredServer.ip).toEqual('199.111.111.1');
expect(component.discoveredServer.port).toEqual(3333);
}));
it('should get first server from discovered and with already added', fakeAsync(() => {
mockedServerService.servers.push(server)
expect(component.discoveredServer).toBeUndefined();
component.discoverFirstAvailableServer();
tick();
expect(component.discoveredServer).toBeUndefined();
}));
});
describe("accepting and ignoring found server", () => {
let server: Server;
beforeEach(() => {
server = new Server();
server.ip = '199.111.111.1',
server.port = 3333;
component.discoveredServer = server;
});
describe("accept", () => {
it("should add new server", fakeAsync(() => {
component.accept(server);
tick();
expect(component.discoveredServer).toBeNull();
expect(mockedServerService.servers[0].ip).toEqual('199.111.111.1');
expect(mockedServerService.servers[0].name).toEqual('199.111.111.1');
}));
});
describe("ignore", () => {
it("should reject server", fakeAsync(() => {
component.ignore(server);
tick();
expect(component.discoveredServer).toBeNull();
}));
});
});
});

View File

@ -0,0 +1,92 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from "rxjs/Rx";
import { map } from "rxjs//operators";
import { Server } from "../../../models/server";
import { VersionService } from "../../../services/version.service";
import { Version } from "../../../models/version";
import { forkJoin } from 'rxjs';
import { ServerService } from '../../../services/server.service';
import { ServerDatabase } from '../../../services/server.database';
@Component({
selector: 'app-server-discovery',
templateUrl: './server-discovery.component.html',
styleUrls: ['./server-discovery.component.scss']
})
export class ServerDiscoveryComponent implements OnInit {
private defaultServers = [{
ip: '127.0.0.1',
port: 3080
}
];
discoveredServer: Server;
constructor(
private versionService: VersionService,
private serverService: ServerService,
private serverDatabase: ServerDatabase
) {}
ngOnInit() {
this.discoverFirstAvailableServer();
}
discoverFirstAvailableServer() {
forkJoin(
Observable.fromPromise(this.serverService.findAll()).pipe(map((s: Server[]) => s)),
this.discovery()
).subscribe(([local, discovered]) => {
local.forEach((added) => {
discovered = discovered.filter((server) => {
return !(server.ip == added.ip && server.port == added.port);
});
});
if(discovered.length > 0) {
this.discoveredServer = discovered.shift();
}
});
}
discovery(): Observable<Server[]> {
const queries: Observable<Server>[] = [];
this.defaultServers.forEach((testServer) => {
queries.push(this.isServerAvailable(testServer.ip, testServer.port).catch((err) => {
return Observable.of(null);
}));
});
return new Observable<Server[]>((observer) => {
forkJoin(queries).subscribe((discoveredServers) => {
observer.next(discoveredServers.filter((s) => s != null));
observer.complete();
});
});
}
isServerAvailable(ip: string, port: number): Observable<Server> {
const server = new Server();
server.ip = ip;
server.port = port;
return this.versionService.get(server).flatMap((version: Version) => Observable.of(server));
}
ignore(server: Server) {
this.discoveredServer = null;
}
accept(server: Server) {
if(server.name == null) {
server.name = server.ip;
}
this.serverService.create(server).then((created: Server) => {
this.serverDatabase.addServer(created);
this.discoveredServer = null;
});
}
}

View File

@ -3,6 +3,9 @@
<h1>Servers</h1>
</div>
<div class="default-content">
<app-server-discovery></app-server-discovery>
<mat-divider></mat-divider>
<div class="example-container mat-elevation-z8">
<mat-table #table [dataSource]="dataSource">

View File

@ -1,19 +1,14 @@
import { Component, Inject, OnInit } from '@angular/core';
import { Component, Inject, OnInit, Injectable } from '@angular/core';
import { DataSource } from "@angular/cdk/collections";
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { Observable, BehaviorSubject, merge } from "rxjs";
import { map } from "rxjs/operators";
// import 'rxjs/add/operator/startWith';
// import 'rxjs/add/observable/merge';
// import 'rxjs/add/operator/map';
// import 'rxjs/add/operator/debounceTime';
// import 'rxjs/add/operator/distinctUntilChanged';
// import 'rxjs/add/observable/fromEvent';
import { Server } from "../../models/server";
import { ServerService } from "../../services/server.service";
import { ServerDatabase } from '../../services/server.database';
@Component({
@ -22,11 +17,13 @@ import { ServerService } from "../../services/server.service";
styleUrls: ['./servers.component.css']
})
export class ServersComponent implements OnInit {
serverDatabase = new ServerDatabase();
dataSource: ServerDataSource;
displayedColumns = ['id', 'name', 'ip', 'port', 'actions'];
constructor(private dialog: MatDialog, private serverService: ServerService) {}
constructor(
private dialog: MatDialog,
private serverService: ServerService,
private serverDatabase: ServerDatabase) {}
ngOnInit() {
this.serverService.findAll().then((servers: Server[]) => {
@ -89,32 +86,6 @@ export class AddServerDialogComponent implements OnInit {
}
export class ServerDatabase {
dataChange: BehaviorSubject<Server[]> = new BehaviorSubject<Server[]>([]);
get data(): Server[] {
return this.dataChange.value;
}
public addServer(server: Server) {
const servers = this.data.slice();
servers.push(server);
this.dataChange.next(servers);
}
public addServers(servers: Server[]) {
this.dataChange.next(servers);
}
public remove(server: Server) {
const index = this.data.indexOf(server);
if (index >= 0) {
this.data.splice(index, 1);
this.dataChange.next(this.data.slice());
}
}
}
export class ServerDataSource extends DataSource<Server> {
constructor(private serverDatabase: ServerDatabase) {
super();

View File

@ -0,0 +1,33 @@
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { Server } from "../models/server";
@Injectable()
export class ServerDatabase {
dataChange: BehaviorSubject<Server[]> = new BehaviorSubject<Server[]>([]);
constructor() {}
get data(): Server[] {
return this.dataChange.value;
}
public addServer(server: Server) {
const servers = this.data.slice();
servers.push(server);
this.dataChange.next(servers);
}
public addServers(servers: Server[]) {
this.dataChange.next(servers);
}
public remove(server: Server) {
const index = this.data.indexOf(server);
if (index >= 0) {
this.data.splice(index, 1);
this.dataChange.next(this.data.slice());
}
}
}

View File

@ -5,9 +5,20 @@ import { Server } from "../models/server";
import { IndexedDbService } from "./indexed-db.service";
import { AngularIndexedDB } from "angular2-indexeddb";
import Spy = jasmine.Spy;
import { resolve } from 'path';
import { reject } from 'q';
export class MockedServerService {
public servers: Server[] = [];
public create(server: Server) {
return new Promise((resolve, reject) => {
this.servers.push(server);
resolve(server);
});
}
public get(server_id: number) {
const server = new Server();
server.id = server_id;
@ -21,6 +32,12 @@ export class MockedServerService {
resolve(server);
});
}
public findAll() {
return new Promise((resolve, reject) => {
resolve(this.servers);
});
}
}

View File

@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
import {IndexedDbService} from "./indexed-db.service";
import {Server} from "../models/server";
import { Observable } from "rxjs";
@Injectable()

View File

@ -7,6 +7,15 @@ import { Server } from '../models/server';
import { getTestServer } from './testing';
import { VersionService } from './version.service';
import { AppTestingModule } from "../testing/app-testing/app-testing.module";
import {Observable} from "rxjs/Rx";
export class MockedVersionService {
public response: Observable<any>;
public get(server: Server) {
return this.response;
}
}
describe('VersionService', () => {

View File

@ -2,20 +2,21 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>GNS3 Web UI Demo</title>
<title>GNS3 Web UI</title>
<!-- It's important to have base here because of the script below //-->
<base href="/">
<script>
var userAgent = navigator.userAgent.toLowerCase();
// in case we're running in electron because we need it for resources
//in case we're running in electron because we need it for resources
if (userAgent.indexOf(' electron/') > -1) {
var base = document.getElementsByTagName('base');
if(base.length > 0) {
base.href = document.location;
base.href ='./';
}
else {
document.write('<base href="' + document.location + '" />');
document.write('<base href="./" />');
}
}
</script>