mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2025-02-20 17:52:46 +00:00
Merge branch 'master' into e2e
This commit is contained in:
commit
0d1739e63e
@ -61,6 +61,7 @@ import { PageNotFoundComponent } from './components/page-not-found/page-not-foun
|
||||
import { Gns3vmComponent } from './components/preferences/gns3vm/gns3vm.component';
|
||||
import { DirectLinkComponent } from './components/direct-link/direct-link.component';
|
||||
import { SystemStatusComponent } from './components/system-status/system-status.component';
|
||||
import { ProjectMapGuard } from './guards/project-map-guard';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -140,7 +141,9 @@ const routes: Routes = [
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'server/:server_id/project/:project_id', component: ProjectMapComponent,
|
||||
path: 'server/:server_id/project/:project_id',
|
||||
component: ProjectMapComponent,
|
||||
canActivate: [ProjectMapGuard]
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
@ -149,7 +152,7 @@ const routes: Routes = [
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled'})],
|
||||
imports: [RouterModule.forRoot(routes, { anchorScrolling: 'enabled', enableTracing: true, scrollPositionRestoration: 'enabled'})],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule {}
|
||||
|
@ -8,6 +8,7 @@ import { PersistenceService } from 'angular-persistence';
|
||||
import { ElectronService, NgxElectronModule } from 'ngx-electron';
|
||||
import createSpyObj = jasmine.createSpyObj;
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ProgressService } from './common/progress/progress.service';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
@ -19,7 +20,7 @@ describe('AppComponent', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [RouterTestingModule, MatIconModule, NgxElectronModule],
|
||||
providers: [SettingsService, PersistenceService],
|
||||
providers: [SettingsService, PersistenceService, ProgressService],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
||||
|
@ -4,6 +4,8 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
import { ElectronService } from 'ngx-electron';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
import { ThemeService } from './services/theme.service';
|
||||
import { Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router';
|
||||
import { ProgressService } from './common/progress/progress.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -16,10 +18,16 @@ export class AppComponent implements OnInit {
|
||||
sanitizer: DomSanitizer,
|
||||
private settingsService: SettingsService,
|
||||
private electronService: ElectronService,
|
||||
private themeService: ThemeService
|
||||
private themeService: ThemeService,
|
||||
private router: Router,
|
||||
private progressService: ProgressService
|
||||
) {
|
||||
iconReg.addSvgIcon('gns3', sanitizer.bypassSecurityTrustResourceUrl('./assets/gns3_icon.svg'));
|
||||
iconReg.addSvgIcon('gns3black', sanitizer.bypassSecurityTrustResourceUrl('./assets/gns3_icon_black.svg'));
|
||||
|
||||
router.events.subscribe((value) => {
|
||||
this.checkEvent(value);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -35,4 +43,16 @@ export class AppComponent implements OnInit {
|
||||
this.themeService.setDarkMode(true);
|
||||
}
|
||||
}
|
||||
|
||||
checkEvent(routerEvent) : void {
|
||||
if (routerEvent instanceof NavigationStart) {
|
||||
this.progressService.activate();
|
||||
}
|
||||
|
||||
else if (routerEvent instanceof NavigationEnd ||
|
||||
routerEvent instanceof NavigationCancel ||
|
||||
routerEvent instanceof NavigationError) {
|
||||
this.progressService.deactivate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +266,7 @@ import { StatusChartComponent } from './components/system-status/status-chart/st
|
||||
import { NgCircleProgressModule } from 'ng-circle-progress';
|
||||
import { OpenFileExplorerActionComponent } from './components/project-map/context-menu/actions/open-file-explorer/open-file-explorer-action.component';
|
||||
import { NgxChildProcessModule } from 'ngx-childprocess';
|
||||
import { ProjectMapGuard } from './guards/project-map-guard';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -539,6 +540,7 @@ if (environment.production) {
|
||||
Gns3vmService,
|
||||
ThemeService,
|
||||
GoogleAnalyticsService,
|
||||
ProjectMapGuard,
|
||||
Title
|
||||
],
|
||||
entryComponents: [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, ViewChild, ElementRef, OnInit, Input, EventEmitter, OnDestroy, Renderer2 } from '@angular/core';
|
||||
import { Component, ViewChild, ElementRef, OnInit, Input, EventEmitter, OnDestroy, Renderer2, NgZone } from '@angular/core';
|
||||
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||
import { TextAddedDataEvent, TextEditedDataEvent } from '../../events/event-source';
|
||||
import { ToolsService } from '../../../services/tools.service';
|
||||
@ -55,7 +55,8 @@ export class TextEditorComponent implements OnInit, OnDestroy {
|
||||
private linksDataSource: LinksDataSource,
|
||||
private nodesDataSource: NodesDataSource,
|
||||
private selectionManager: SelectionManager,
|
||||
private fontFixer: FontFixer
|
||||
private fontFixer: FontFixer,
|
||||
private ngZone: NgZone
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -63,8 +64,8 @@ export class TextEditorComponent implements OnInit, OnDestroy {
|
||||
isActive ? this.activateTextAdding() : this.deactivateTextAdding();
|
||||
});
|
||||
|
||||
this.activateTextEditingForDrawings();
|
||||
this.activateTextEditingForNodeLabels();
|
||||
this.ngZone.runOutsideAngular(this.activateTextEditingForDrawings.bind(this));
|
||||
this.ngZone.runOutsideAngular(this.activateTextEditingForNodeLabels.bind(this));
|
||||
}
|
||||
|
||||
activateTextAdding() {
|
||||
|
@ -17,6 +17,7 @@ export class ShowNodeActionComponent {
|
||||
showNode() {
|
||||
const dialogRef = this.dialog.open(InfoDialogComponent, {
|
||||
width: '600px',
|
||||
maxHeight: '600px',
|
||||
autoFocus: false,
|
||||
disableClose: true
|
||||
});
|
||||
|
@ -73,6 +73,8 @@ export class ProjectsComponent implements OnInit {
|
||||
|
||||
this.settings = this.settingsService.getAll();
|
||||
|
||||
this.projectService.projectListSubject.subscribe(() => this.refresh());
|
||||
|
||||
let gns3vmConfig = localStorage.getItem('gns3vmConfig');
|
||||
if (this.electronService.isElectronApp && gns3vmConfig!=='configured') {
|
||||
const dialogRef = this.dialog.open(ConfigureGns3VMDialogComponent, {
|
||||
|
35
src/app/guards/project-map-guard.ts
Normal file
35
src/app/guards/project-map-guard.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||
import { ProjectMapComponent } from '../components/project-map/project-map.component';
|
||||
import { Observable, pipe, timer, from } from 'rxjs';
|
||||
import { ProjectService } from '../services/project.service';
|
||||
import { Server } from '../models/server';
|
||||
import { ServerService } from '../services/server.service';
|
||||
import { switchMap, map } from 'rxjs/operators';
|
||||
import { ToasterService } from '../services/toaster.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectMapGuard implements CanActivate {
|
||||
constructor(
|
||||
private projectService: ProjectService,
|
||||
private serverService: ServerService,
|
||||
private toasterService: ToasterService
|
||||
) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
const server_id = route.paramMap.get("server_id");
|
||||
const project_id = route.paramMap.get("project_id");
|
||||
|
||||
return from(this.serverService.get(parseInt(server_id, 10))).pipe(
|
||||
switchMap(response => this.projectService.list(response as Server)),
|
||||
map(response => {
|
||||
let projectToOpen = response.find(n => n.project_id === project_id);
|
||||
if (projectToOpen) return true;
|
||||
|
||||
this.toasterService.error('Project could not be opened');
|
||||
this.projectService.projectListUpdated();
|
||||
return false;
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
@ -67,7 +67,7 @@ export class InfoService {
|
||||
node.node_type === "iou") {
|
||||
return 'Command line information is not supported for this type of node.';
|
||||
} else {
|
||||
if (node.status === 'started') {
|
||||
if (node.command_line) {
|
||||
return node.command_line;
|
||||
} else {
|
||||
return 'Please start the node in order to get the command line information.';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Project } from '../models/project';
|
||||
import { Node } from '../cartography/models/node';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Link } from '../models/link';
|
||||
import { Server } from '../models/server';
|
||||
import { HttpServer } from './http-server.service';
|
||||
@ -10,8 +10,14 @@ import { SettingsService } from './settings.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectService {
|
||||
public projectListSubject = new Subject<boolean>();
|
||||
|
||||
constructor(private httpServer: HttpServer, private settingsService: SettingsService) {}
|
||||
|
||||
projectListUpdated() {
|
||||
this.projectListSubject.next(true);
|
||||
}
|
||||
|
||||
get(server: Server, project_id: string) {
|
||||
return this.httpServer.get<Project>(server, `/projects/${project_id}`);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { getTestServer } from './testing';
|
||||
import { SymbolService } from './symbol.service';
|
||||
import { Symbol } from '../models/symbol';
|
||||
import { AppTestingModule } from '../testing/app-testing/app-testing.module';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('SymbolService', () => {
|
||||
let httpClient: HttpClient;
|
||||
@ -50,20 +51,11 @@ describe('SymbolService', () => {
|
||||
}));
|
||||
|
||||
it('should load symbols', inject([SymbolService], (service: SymbolService) => {
|
||||
service.load(server).subscribe();
|
||||
spyOn(service, 'load').and.returnValue(of([]));
|
||||
|
||||
const req = httpTestingController.expectOne('http://127.0.0.1:3080/v2/symbols');
|
||||
service.list(server).subscribe();
|
||||
|
||||
req.flush([{ symbol_id: 'myid' }]);
|
||||
|
||||
const raw = httpTestingController.expectOne('http://127.0.0.1:3080/v2/symbols/myid/raw');
|
||||
raw.flush('myraw');
|
||||
|
||||
service.symbols.subscribe(symbols => {
|
||||
expect(symbols.length).toEqual(1);
|
||||
expect(symbols[0].symbol_id).toEqual('myid');
|
||||
expect(symbols[0].raw).toEqual('myraw');
|
||||
});
|
||||
expect(service.load).toHaveBeenCalled();
|
||||
}));
|
||||
|
||||
it('should get symbols', inject([SymbolService], (service: SymbolService) => {
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, forkJoin, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, forkJoin, Observable, of } from 'rxjs';
|
||||
|
||||
import { Symbol } from '../models/symbol';
|
||||
import { Server } from '../models/server';
|
||||
import { HttpServer } from './http-server.service';
|
||||
import { shareReplay } from 'rxjs/operators';
|
||||
|
||||
const CACHE_SIZE = 1;
|
||||
|
||||
@Injectable()
|
||||
export class SymbolService {
|
||||
public symbols: BehaviorSubject<Symbol[]> = new BehaviorSubject<Symbol[]>([]);
|
||||
private cache: Observable<Symbol[]>;
|
||||
|
||||
constructor(private httpServer: HttpServer) {}
|
||||
|
||||
@ -16,25 +20,22 @@ export class SymbolService {
|
||||
}
|
||||
|
||||
add(server: Server, symbolName: string, symbol: string) {
|
||||
this.cache = null;
|
||||
return this.httpServer.post(server, `/symbols/${symbolName}/raw`, symbol)
|
||||
}
|
||||
|
||||
load(server: Server): Observable<Symbol[]> {
|
||||
const subscription = this.list(server).subscribe((symbols: Symbol[]) => {
|
||||
const streams = symbols.map(symbol => this.raw(server, symbol.symbol_id));
|
||||
forkJoin(streams).subscribe(results => {
|
||||
symbols.forEach((symbol: Symbol, i: number) => {
|
||||
symbol.raw = results[i];
|
||||
});
|
||||
this.symbols.next(symbols);
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
});
|
||||
return this.symbols.asObservable();
|
||||
return this.httpServer.get<Symbol[]>(server, '/symbols');
|
||||
}
|
||||
|
||||
list(server: Server) {
|
||||
return this.httpServer.get<Symbol[]>(server, '/symbols');
|
||||
if(!this.cache) {
|
||||
this.cache = this.load(server).pipe(
|
||||
shareReplay(CACHE_SIZE)
|
||||
);
|
||||
}
|
||||
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
raw(server: Server, symbol_id: string) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user