Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) Ping functionality for the UI #1590

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions static/skywire-manager-src/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import { RewardsAddressComponent } from './components/pages/node/node-info/node-
import { BulkRewardAddressChangerComponent } from './components/layout/bulk-reward-address-changer/bulk-reward-address-changer.component';
import { UserAppSettingsComponent } from './components/pages/node/apps/node-apps/user-app-settings/user-app-settings.component';
import { NodeLogsComponent } from './components/pages/node/actions/node-logs/node-logs.component';
import { PingDialogComponent } from './components/pages/node/routing/ping-dialog/ping-dialog.component';
import { SkychatSettingsComponent } from './components/pages/node/apps/node-apps/skychat-settings/skychat-settings.component';
import { TabSelectorComponent } from './components/layout/tab-selector/tab-selector.component';

Expand Down Expand Up @@ -180,6 +181,7 @@ const globalRippleConfig: RippleGlobalOptions = {
BulkRewardAddressChangerComponent,
UserAppSettingsComponent,
NodeLogsComponent,
PingDialogComponent,
SkychatSettingsComponent,
TabSelectorComponent,
],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<app-dialog [headline]="'ping.title' | translate" [dialog]="dialogRef" [disableDismiss]="disableDismiss">
<!-- Loading animation. -->
<app-loading-indicator [showWhite]="false" *ngIf="checking"></app-loading-indicator>

<!-- Form. -->
<form [formGroup]="form" *ngIf="!checking && result === null">
<mat-form-field [ngClass]="{'element-disabled' : disableDismiss}">
<div class="field-container">
<label class="field-label" for="remoteKey">{{ 'ping.remote-visor-key' | translate }}</label>
<input
formControlName="remoteKey"
maxlength="66"
#firstInput
matInput
>
</div>
<mat-error>
<ng-container *ngIf="!this.form.get('remoteKey').hasError('pattern');else hexError">
<span>{{ 'ping.remote-key-length-error' | translate }}</span>
</ng-container>
</mat-error>
<ng-template #hexError>
<span>{{ 'ping.remote-key-chars-error' | translate }}</span>
</ng-template>
</mat-form-field>
</form>

<!-- Result. -->
<div *ngIf="!checking && result !== null" class="info-dialog">
<div class="title mt-0">
<mat-icon [inline]="true">import_export</mat-icon>{{ 'ping.result-title' | translate }}
</div>
<div class="item">
<span>{{ 'ping.result' | translate }}</span> {{ result }}ms.
</div>
</div>

<app-button
#button
(action)="process()"
[disabled]="result === null && !form.valid"
color="primary"
class="float-right"
*ngIf="!checking"
>
<ng-container *ngIf="result === null">
{{ 'ping.ping-button' | translate }}
</ng-container>
<ng-container *ngIf="result !== null">
{{ 'ping.make-another-test' | translate }}
</ng-container>
</app-button>
</app-dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { Component, OnInit, ViewChild, OnDestroy, ElementRef } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { MatDialogRef, MatDialog, MatDialogConfig } from '@angular/material/dialog';
import { Subscription } from 'rxjs';

import { AppConfig } from 'src/app/app.config';
import { processServiceError } from 'src/app/utils/errors';
import { NodeService } from 'src/app/services/node.service';
import { ButtonComponent } from 'src/app/components/layout/button/button.component';
import { SnackbarService } from 'src/app/services/snackbar.service';
import { NodeComponent } from '../../node.component';

/**
* Modal for makinbg a ping request to a remote node.
*/
@Component({
selector: 'app-ping-dialog-transport',
templateUrl: './ping-dialog.component.html',
styleUrls: ['./ping-dialog.component.scss']
})
export class PingDialogComponent implements OnInit, OnDestroy {
@ViewChild('button') button: ButtonComponent;
@ViewChild('firstInput') firstInput: ElementRef;
form: UntypedFormGroup;

// If the ping operation is being made.
checking = false;
// Last result obtained in ms. If null, the form must be shown.
result: number = null;

private operationSubscription: Subscription;

/**
* Opens the modal window. Please use this function instead of opening the window "by hand".
*/
public static openDialog(dialog: MatDialog): MatDialogRef<PingDialogComponent, any> {
const config = new MatDialogConfig();
config.autoFocus = false;
config.width = AppConfig.mediumModalWidth;

return dialog.open(PingDialogComponent, config);
}

constructor(
private formBuilder: UntypedFormBuilder,
public dialogRef: MatDialogRef<PingDialogComponent>,
private snackbarService: SnackbarService,
private nodeService: NodeService,
) { }

ngOnInit() {
this.form = this.formBuilder.group({
remoteKey: ['', Validators.compose([
Validators.required,
Validators.minLength(66),
Validators.maxLength(66),
Validators.pattern('^[0-9a-fA-F]+$')])
]
});
}

ngOnDestroy() {
if (this.operationSubscription) {
this.operationSubscription.unsubscribe();
}
}

/**
* If true, all the ways provided by default by the UI for closing the modal window are disabled.
*/
get disableDismiss(): boolean {
return this.checking;
}

process() {
if (this.result === null) {
// Make the request.
this.ping();
} else {
// Show the form again.
this.checking = false;
this.result = null;
}
}

/**
* Makes the ping request.
*/
ping() {
if (!this.form.valid || this.button.disabled) {
return;
}

this.checking = true;

const remotePk: string = this.form.get('remoteKey').value;

this.operationSubscription = this.nodeService.ping(NodeComponent.getCurrentNodeKey(), remotePk).subscribe(response => {
this.result = response[0];
this.checking = false;
}, err => {
err = processServiceError(err);
this.snackbarService.showError(err);
this.checking = false;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
[showShortList]="true">
</app-transport-list>

<app-button color="primary" (action)="openPingDialog()" class="ping-button" [forDarkBackground]="true">
{{ 'ping.title' | translate }}
</app-button>

<app-route-list
[routes]="routes"
[showShortList]="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.ping-button {
display: block;
margin-top: 15px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Subscription } from 'rxjs';
import { Node, Route } from '../../../../app.datatypes';
import { NodeComponent } from '../node.component';
import { PageBaseComponent } from 'src/app/utils/page-base';
import { PingDialogComponent } from './ping-dialog/ping-dialog.component';
import { MatDialog } from '@angular/material/dialog';

/**
* Page that shows the routing summary. It is a subpage of the Node page.
Expand All @@ -20,6 +22,10 @@ export class RoutingComponent extends PageBaseComponent implements OnInit, OnDes

private dataSubscription: Subscription;

constructor(private dialog: MatDialog) {
super();
}

ngOnInit() {
// Get the node data from the parent page.
this.dataSubscription = NodeComponent.currentNode.subscribe((node: Node) => {
Expand All @@ -34,4 +40,8 @@ export class RoutingComponent extends PageBaseComponent implements OnInit, OnDes
ngOnDestroy() {
this.dataSubscription.unsubscribe();
}

openPingDialog() {
PingDialogComponent.openDialog(this.dialog);
}
}
13 changes: 13 additions & 0 deletions static/skywire-manager-src/src/app/services/node.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,19 @@ export class NodeService {
return this.apiService.post(`visors/${nodeKey}/shutdown`);
}

/**
* Pings a remote node
*/
ping(nodeKey: string, remotePk: string): Observable<any> {
const data = {
remote_pk: remotePk,
size: 3,
tries: 2,
};

return this.apiService.post(`visors/${nodeKey}/ping-visor`, data);
}

/**
* Checks if a node is currently being updated.
*/
Expand Down
12 changes: 12 additions & 0 deletions static/skywire-manager-src/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,18 @@
}
},

"ping": {
"button-text": "Ping specific visor",
"title": "Ping Specific Visor",
"remote-visor-key": "Remote visor key",
"result-title": "Result",
"result": "Response obtained in:",
"ping-button": "Ping",
"remote-key-length-error": "The public key must be 66 characters long.",
"remote-key-chars-error": "The public key must only contain hexadecimal characters.",
"make-another-test": "Make another test"
},

"routes": {
"title": "Routes",
"info": "Paths used to reach the remote visors to which transports have been established. Routes are automatically generated as needed.",
Expand Down
12 changes: 12 additions & 0 deletions static/skywire-manager-src/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,18 @@
}
},

"ping": {
"button-text": "Ping a un visor específico",
"title": "Ping a un Visor Específico",
"remote-visor-key": "Llave del visor remoto",
"result-title": "Resultado",
"result": "Respuesta obtenida en:",
"ping-button": "Ping",
"remote-key-length-error": "La llave pública debe tener 66 caracteres.",
"remote-key-chars-error": "La llave pública sólo debe contener caracteres hexadecimales.",
"make-another-test": "Realizar otra prueba"
},

"routes": {
"title": "Rutas",
"info": "Caminos utilizados para llegar a los visores remotos con los que se han establecido transportes. Las rutas se generan automáticamente según sea necesario.",
Expand Down
12 changes: 12 additions & 0 deletions static/skywire-manager-src/src/assets/i18n/es_base.json
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,18 @@
}
},

"ping": {
"button-text": "Ping specific visor",
"title": "Ping Specific Visor",
"remote-visor-key": "Remote visor key",
"result-title": "Result",
"result": "Response obtained in:",
"ping-button": "Ping",
"remote-key-length-error": "The public key must be 66 characters long.",
"remote-key-chars-error": "The public key must only contain hexadecimal characters.",
"make-another-test": "Make another test"
},

"routes": {
"title": "Routes",
"info": "Paths used to reach the remote visors to which transports have been established. Routes are automatically generated as needed.",
Expand Down
Loading