mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-06-23 08:55:32 +00:00
Merge with develop
This commit is contained in:
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
|
@ -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']);
|
||||
}));
|
||||
});
|
||||
|
@ -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>
|
@ -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();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
@ -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">
|
||||
|
@ -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();
|
||||
|
33
src/app/services/server.database.ts
Normal file
33
src/app/services/server.database.ts
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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', () => {
|
||||
|
@ -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>
|
||||
|
Reference in New Issue
Block a user