Skip to content

Commit

Permalink
Add discovered, online, offline states to nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
mahansky committed Nov 23, 2018
1 parent 506e2e9 commit 1a5afe5
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 75 deletions.
9 changes: 8 additions & 1 deletion static/skywire-manager-src/src/app/app.datatypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ export interface NodeInfo {
}

export interface NodeStatusInfo extends Node {
online: boolean;
status: NodeStatus;
}

export enum NodeStatus {
DISCOVERED = 'discovered',
ONLINE = 'online',
OFFLINE = 'offline',
UNKNOWN = 'unknown',
}

export interface NodeData {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,7 @@
<ng-container matColumnDef="enabled">
<th mat-header-cell *matHeaderCellDef class="bold"></th>
<td mat-cell *matCellDef="let node;">
<span
class="dot-green"
*ngIf="node.online === true"
[matTooltip]="'node.online-tooltip' | translate"
></span>
<span
class="dot-red"
*ngIf="node.online === false"
[matTooltip]="'node.offline-tooltip' | translate"
></span>
<span
class="dot-gray"
*ngIf="node.online === undefined"
[matTooltip]="'node.unknown-tooltip' | translate"
></span>
<span [class]="nodeStatusClass(node)" [matTooltip]="'node.statuses.' + node.status + '-tooltip' | translate"></span>
</td>
</ng-container>

Expand All @@ -50,7 +36,10 @@

<ng-container matColumnDef="start_time">
<th mat-header-cell *matHeaderCellDef class="bold">{{ 'nodes.running' | translate }}</th>
<td mat-cell *matCellDef="let node;">{{node.start_time | relativeTime:true}}</td>
<td mat-cell *matCellDef="let node;">
<span *ngIf="node.status === nodeStatus.DISCOVERED || node.status === nodeStatus.ONLINE"></span>
{{ node.start_time | relativeTime:true }}
</td>
</ng-container>

<ng-container matColumnDef="actions">
Expand All @@ -59,15 +48,33 @@
<button (click)="showEditLabelDialog(node)" mat-icon-button [matTooltip]="'nodes.edit-label' | translate">
<mat-icon>short_text</mat-icon>
</button>
<button (click)="viewNode(node)" mat-icon-button [matTooltip]="'nodes.view-node' | translate">
<button
*ngIf="node.status === nodeStatus.DISCOVERED || node.status === nodeStatus.ONLINE"
(click)="viewNode(node)"
mat-icon-button
[matTooltip]="'nodes.view-node' | translate"
>
<mat-icon>chevron_right</mat-icon>
</button>
<button
*ngIf="node.status === nodeStatus.OFFLINE"
(click)="deleteNode(node)"
mat-icon-button
[matTooltip]="'nodes.delete-node' | translate"
>
<mat-icon>close</mat-icon>
</button>
</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let node; columns: displayedColumns;" (click)="viewNode(node)" class="cursor-pointer"></tr>
</table>

<div class="nodes-empty table-white container-elevated-white" *ngIf="dataSource.data.length === 0">
<i class="material-icons">warning</i>
{{ 'nodes.empty' | translate }}
</div>
</div>
</div>
<div class="col-md-1"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,20 @@
font-weight: bold;
}

table {
table, .nodes-empty {
box-shadow: none !important;
}

.nodes-empty {
color: theme-color(light-gray);
font-size: 13px;

i {
display: inline-block;
vertical-align: middle;
margin-right: 10px;
}
}
}

.actions {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import {NodeService} from '../../../services/node.service';
import {Node, NodeStatusInfo} from '../../../app.datatypes';
import { NodeService } from '../../../services/node.service';
import { Node, NodeStatus, NodeStatusInfo } from '../../../app.datatypes';
import { Subscription } from 'rxjs';
import { MatDialog, MatTableDataSource } from '@angular/material';
import {Router} from '@angular/router';
import { Router } from '@angular/router';
import { ButtonComponent } from '../../layout/button/button.component';
import { EditLabelComponent } from '../../layout/edit-label/edit-label.component';
import { TranslateService } from '@ngx-translate/core';
import {ErrorsnackbarService} from '../../../services/errorsnackbar.service';
import { ErrorsnackbarService } from '../../../services/errorsnackbar.service';
import { StorageService } from '../../../services/storage.service';

@Component({
selector: 'app-node-list',
Expand All @@ -18,6 +19,7 @@ export class NodeListComponent implements OnInit, OnDestroy {
@ViewChild('refreshButton') refreshButton: ButtonComponent;
dataSource = new MatTableDataSource<NodeStatusInfo>();
displayedColumns: string[] = ['enabled', 'index', 'label', 'key', 'start_time', 'actions'];
nodeStatus = NodeStatus;
private subscriptions: Subscription;

constructor(
Expand All @@ -26,6 +28,7 @@ export class NodeListComponent implements OnInit, OnDestroy {
private errorSnackBar: ErrorsnackbarService,
private dialog: MatDialog,
private translate: TranslateService,
private storageService: StorageService,
) { }

ngOnInit() {
Expand Down Expand Up @@ -64,8 +67,28 @@ export class NodeListComponent implements OnInit, OnDestroy {
return this.nodeService.getLabel(node);
}

viewNode(node) {
this.router.navigate(['nodes', node.key]);
viewNode(node: NodeStatusInfo) {
if (node.status === NodeStatus.DISCOVERED || node.status === NodeStatus.ONLINE) {
this.router.navigate(['nodes', node.key]);
}
}

deleteNode(node: Node) {
this.storageService.removeNode(node.key);
this.refresh();
}

nodeStatusClass(node: NodeStatusInfo) {
switch (node.status) {
case NodeStatus.DISCOVERED:
return 'dot-green';
case NodeStatus.ONLINE:
return 'dot-yellow';
case NodeStatus.OFFLINE:
return 'dot-red';
default:
return 'dot-gray';
}
}

private onError() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
<ul class="node-status-bar list-inline d-flex justify-content-around mb-0">
<li class="status-text status-online list-inline-item d-flex align-items-center justify-content-between"
matTooltip="{{onlineTooltip}}">
[matTooltip]="onlineTooltip">
<mat-icon>network_check</mat-icon>
<div class="d-flex flex-column mw-0">
<span class="font-mini">{{ 'node.status' | translate }}</span>
<span id="nodeOnlineStatus">{{(isOnline ? 'node.online' : 'node.offline') | translate }}</span>
<span id="nodeOnlineStatus">
{{ (isDiscovered ? 'node.statuses.discovered' : 'node.statuses.online') | translate }}
</span>
</div>
</li>
<li class="separator"></li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Component, Input, OnChanges, OnInit} from '@angular/core';
import {NodeData} from '../../../../app.datatypes';
import {TranslateService} from '@ngx-translate/core';
import {isOnline as checkOnline} from '../../../../utils/nodeUtils';
import {isDiscovered} from '../../../../utils/nodeUtils';

@Component({
selector: 'app-node-status-bar',
Expand All @@ -22,17 +22,12 @@ export class NodeStatusBarComponent implements OnInit, OnChanges {
this.getOnlineTooltip();
}

get isOnline(): boolean {
return checkOnline(this.nodeData.info);
get isDiscovered(): boolean {
return isDiscovered(this.nodeData.info);
}

getOnlineTooltip(): void {
let key;
if (this.isOnline) {
key = 'node.online-tooltip';
} else {
key = 'node.offline-tooltip';
}
this.translate.get(key).subscribe((text) => this.onlineTooltip = text);
this.translate.get(this.isDiscovered ? 'node.statuses.discovered-tooltip' : 'node.statuses.online-tooltip')
.subscribe((text: string) => this.onlineTooltip = text);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { NodeService } from '../../../services/node.service';
import {Node, NodeData} from '../../../app.datatypes';
import { Node, NodeData, NodeStatus } from '../../../app.datatypes';
import { ActivatedRoute, Router } from '@angular/router';
import { MatDialog} from '@angular/material';
import { MatDialog } from '@angular/material';
import { Subscription } from 'rxjs/internal/Subscription';
import { TranslateService } from '@ngx-translate/core';
import {ErrorsnackbarService} from '../../../services/errorsnackbar.service';
import { ErrorsnackbarService } from '../../../services/errorsnackbar.service';

@Component({
selector: 'app-node',
Expand Down Expand Up @@ -97,7 +97,7 @@ export class NodeComponent implements OnInit, OnDestroy {
}

get operationalNodesCount(): number {
return this.nodeData.allNodes.filter((node) => node.online === true).length;
return this.nodeData.allNodes.filter((node) => node.status === NodeStatus.DISCOVERED).length;
}

get operationalNodesClass(): string {
Expand Down
89 changes: 66 additions & 23 deletions static/skywire-manager-src/src/app/services/node.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import {Injectable, NgZone} from '@angular/core';
import {bindCallback, forkJoin, interval, Observable, of, Subject, timer, Unsubscribable} from 'rxjs';
import {AutoStartConfig, NodeStatusInfo, Node, NodeApp, NodeData, NodeInfo, SearchResult} from '../app.datatypes';
import { Injectable } from '@angular/core';
import {
forkJoin,
interval,
Observable,
of,
Subject,
timer,
Unsubscribable
} from 'rxjs';
import {
AutoStartConfig,
Node,
NodeApp,
NodeData,
NodeInfo,
NodeStatus,
NodeStatusInfo,
SearchResult
} from '../app.datatypes';
import { ApiService } from './api.service';
import {
catchError,
Expand All @@ -10,11 +27,11 @@ import {
flatMap,
map,
switchMap,
take,
take, tap,
timeout
} from 'rxjs/operators';
import {StorageService} from './storage.service';
import {getNodeLabel, isOnline} from '../utils/nodeUtils';
import { StorageService } from './storage.service';
import { getNodeLabel, isDiscovered } from '../utils/nodeUtils';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -45,18 +62,44 @@ export class NodeService {
*/
getAllNodes(): Observable<NodeStatusInfo[]> {
return this.apiService.get('conn/getAll').pipe(
flatMap(
nodes => forkJoin(nodes.map(node => this.node(node.key).pipe(
map(nodeWithAddress => ({...node, ...nodeWithAddress})),
catchError(() => of({...node}))
)))
),
flatMap(
nodes => forkJoin(nodes.map((node: Node) => this.nodeInfo(node).pipe(
map(nodeInfo => ({...node, online: isOnline(nodeInfo)})),
catchError(() => of({...node, online: undefined})),
)))
)
flatMap(nodes => {
if (nodes.length === 0) {
return of([]);
}

return forkJoin(nodes.map(node => this.node(node.key).pipe(
map(nodeWithAddress => ({...node, ...nodeWithAddress})),
catchError(() => of({...node}))
)));
}),
flatMap(nodes => {
if (nodes.length === 0) {
return of([]);
}

return forkJoin(nodes.map((node: Node) => this.nodeInfo(node).pipe(
map(nodeInfo => ({...node, status: isDiscovered(nodeInfo) ? NodeStatus.DISCOVERED : NodeStatus.ONLINE})),
catchError(() => of({...node, status: NodeStatus.UNKNOWN})),
)));
}),
map((nodes: Node[]) => {
let storedNodes = this.storageService.getNodes();

if (storedNodes.length === 0) {
nodes.forEach(node => this.storageService.addNode(node.key));
storedNodes = this.storageService.getNodes();
}

const allNodes = storedNodes.map(nodeKey => ({ key: nodeKey, status: NodeStatus.OFFLINE }));

return allNodes.reduce((all, current) => {
const existing = nodes.find(node => node.key === current.key);

all.push(existing ? existing : current);

return all;
}, []);
})
);
}

Expand Down Expand Up @@ -118,11 +161,11 @@ export class NodeService {

notifyNodeDataRefreshed(data: any) {
this.currentNodeData.next({
node: { ...data[0], key: this.currentNode.key },
apps: data[1] || [],
info: { ...data[2], transports: data[2].transports || [] },
allNodes: data[3] || []
});
node: { ...data[0], key: this.currentNode.key },
apps: data[1] || [],
info: { ...data[2], transports: data[2].transports || [] },
allNodes: data[3] || []
});
}

requestRefreshNodeData() {
Expand Down
21 changes: 21 additions & 0 deletions static/skywire-manager-src/src/app/services/storage.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';

const KEY_REFRESH_SECONDS = 'KEY_REFRESH_SECONDS';
const KEY_DEFAULT_LANG = 'KEY_DEFAULT_LANG';
const KEY_NODES = 'KEY_NODES';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -40,4 +41,24 @@ export class StorageService {
getDefaultLanguage(): string {
return this.storage.getItem(KEY_DEFAULT_LANG) || 'en';
}

addNode(nodeKey: string) {
const nodes = new Set<string>(this.getNodes());

nodes.add(nodeKey);

this.setNodes(Array.from(nodes));
}

removeNode(nodeKey: string) {
this.setNodes(this.getNodes().filter(n => n !== nodeKey));
}

getNodes(): string[] {
return JSON.parse(this.storage.getItem(KEY_NODES)) || [];
}

private setNodes(nodes: string[]) {
this.storage.setItem(KEY_NODES, JSON.stringify(nodes));
}
}
Loading

0 comments on commit 1a5afe5

Please sign in to comment.