diff --git a/src/app/gui/credentials-modal/credentials-modal.component.html b/src/app/gui/credentials-modal/credentials-modal.component.html
index e1a1677..0719180 100644
--- a/src/app/gui/credentials-modal/credentials-modal.component.html
+++ b/src/app/gui/credentials-modal/credentials-modal.component.html
@@ -21,6 +21,6 @@
-
-
+
+
diff --git a/src/app/gui/uds-gui.service.ts b/src/app/gui/uds-gui.service.ts
index 68dde34..fa0db49 100644
--- a/src/app/gui/uds-gui.service.ts
+++ b/src/app/gui/uds-gui.service.ts
@@ -1,22 +1,31 @@
import { Injectable } from '@angular/core';
+import { timeout } from 'rxjs/operators';
import { ModalComponent, DialogType } from './modal/modal.component';
import { CredentialsModalComponent } from './credentials-modal/credentials-modal.component';
-import { MatDialog } from '@angular/material/dialog';
+import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { Observable, firstValueFrom } from 'rxjs';
-const toPromise = (observable: Observable): Promise => firstValueFrom(observable);
+export const toPromise = (observable: Observable|Promise, wait?: number): Promise => {
+ if (observable instanceof Promise) {
+ return observable;
+ }
+ if (wait) {
+ return firstValueFrom(observable.pipe(timeout(wait)));
+ }
+ return firstValueFrom(observable);
+};
@Injectable()
export class UDSGuiService {
constructor(public dialog: MatDialog) {}
- alert(
+ async alert(
title: string,
message: string,
autoclose = 0,
checkClose: Promise = null
- ) {
+ ): Promise> {
const width = window.innerWidth < 800 ? '80%' : '40%';
const dialogRef = this.dialog.open(ModalComponent, {
width,
@@ -43,7 +52,7 @@ export class UDSGuiService {
return dialogRef.componentInstance.yesno;
}
- askCredentials(username: string, domain: string): Promise<{username: string; password: string; domain: string}> {
+ askCredentials(username: string, domain: string): Promise<{username: string; password: string; domain: string; success: boolean}> {
const dialogRef = this.dialog.open(CredentialsModalComponent, {
data: {
username,
diff --git a/src/app/helpers/plugin.ts b/src/app/helpers/plugin.ts
index 8d88600..4dfb1da 100644
--- a/src/app/helpers/plugin.ts
+++ b/src/app/helpers/plugin.ts
@@ -1,5 +1,7 @@
import { Observable } from 'rxjs';
import { UDSApiServiceType } from '../uds-api.service-type';
+import { toPromise } from '../gui/uds-gui.service';
+import { JSONTransportURLService } from '../types/services';
declare const django: any;
@@ -15,255 +17,15 @@ export class Plugin {
this.delay = api.config.launcher_wait_time;
}
- launchURL(url: string): void {
- let state = 'init';
- // Internal helper for notify errors
- const notifyError = (error?: any) => {
- let msg: string = django.gettext(
- 'Error communicating with your service. Please, retry again.'
- );
- if (typeof error === 'string') {
- msg = error;
- } else if (error.status === 403) {
- // Session timeout
- msg = django.gettext('Your session has expired. Please, login again');
- }
- window.setTimeout(() => {
- this.showAlert(django.gettext('Error'), msg, 5000);
- if (error.status === 403) {
- window.setTimeout(() => {
- this.api.logout();
- }, 5000);
- }
- });
- };
-
+ async launchURL(url: string): Promise {
// If uds url...
if (url.substring(0, 7) === 'udsa://') {
- const params = url.split('//')[1].split('/');
- const alert = this.showAlert(
- django.gettext('Please wait until the service is launched.'),
- django.gettext(
- 'Remember that you will need the UDS client on your platform to access the service.'
- ),
- 0,
- // Now UDS tries to check status
- new Promise((resolve) => {
- let readyTime = 0;
- const checker = () => {
- if (alert.componentInstance) {
- // Not closed dialog...
- this.api.status(params[0], params[1]).then(
- (data) => {
- if (data.status === 'ready') {
- if (!readyTime) {
- readyTime = Date.now(); // Milisecodns
- alert.componentInstance.data.title =
- django.gettext('Service ready');
- alert.componentInstance.data.body = django.gettext(
- 'Launching UDS Client, almost done.'
- );
- } else {
- // If Component took too long...
- if (Date.now() - readyTime > this.delay * 5) {
- // Wait 5 times the default delay
- alert.componentInstance.data.title =
- django.gettext('Service ready') +
- ' - ' +
- django.gettext('UDS Client not launching');
- alert.componentInstance.data.body =
- '' +
- django.gettext(
- 'It seems that you don\'t have UDS Client installed. Please, install it from here:'
- ) +
- ' ' +
- '' +
- django.gettext('UDS Client Download') +
- '';
- }
- }
- window.setTimeout(checker, this.delay); // Recheck after delay seconds
- } else if (data.status === 'accessed') {
- alert.componentInstance.data.body = django.gettext(
- 'Machine ready, waiting for UDS Client'
- );
- resolve(true);
- } else if (data.status === 'running') {
- window.setTimeout(checker, this.delay); // Recheck after delay seconds
- } else {
- resolve(true);
- notifyError();
- }
- },
- (error) => {
- resolve(true);
- notifyError(error);
- }
- );
- }
- };
- const init = () => {
- if (state === 'init') {
- window.setTimeout(init, this.delay);
- } else if (state === 'error' || state === 'stop') {
- return;
- } else {
- window.setTimeout(checker);
- }
- };
- window.setTimeout(init);
- })
- );
-
- this.api.enabler(params[0], params[1]).then(
- (data) => {
- if (data.error) {
- state = 'error';
- // TODO: show the error correctly
- this.api.gui.alert(
- django.gettext('Error launching service'),
- data.error
- );
- } else {
- // Is HTTP access the service returned, or for UDS client?
- if (data.url.startsWith('/')) {
- // If running message window, close it first...
- if (alert.componentInstance) {
- alert.componentInstance.close();
- }
- state = 'stop';
- this.launchURL(data.url);
- return;
- }
- if (window.location.protocol === 'https:') {
- // Ensures that protocol is https also for plugin, fixing if needed UDS provided info
- data.url = data.url.replace('uds://', 'udss://');
- }
- state = 'enabled';
- this.doLaunch(data.url);
- }
- },
- (error) => {
- // Any error on requests will redirect to login
- this.api.logout();
- }
- );
+ await this.processUDSUrl(url);
} else {
- // Custom url, http/https
- const alert = this.showAlert(
- django.gettext('Please wait until the service is launched.'),
- django.gettext(
- 'Your connection is being prepared. It will open on a new window when ready.'
- ),
- 0,
- // Now UDS tries to check status before closing dialog...
- new Promise((resolve) => {
- const checker = () => {
- if (alert.componentInstance) {
- // Not closed dialog...
- this.api.transportUrl(url).then(
- (data) => {
- if (data.url) {
- resolve(true);
- // Extract if credentials modal is requested
- let username = '';
- let domain = '';
- let askCredentials = false;
- let ticket = '';
- let scrambler = '';
-
- if (data.url.indexOf('&creds=') !== -1) {
- askCredentials = true;
- // Extract username and domain "&creds=username@domain"
- const creds = data.url.split('&creds=')[1];
- if (creds.indexOf('@') !== -1) {
- username = creds.split('@')[0];
- domain = creds.split('@')[1];
- } else {
- username = creds;
- }
- // Remove credentials from url
- data.url = data.url.split('&creds=')[0];
- // From "data=..." extract ticket and scrambler that is ticket.scrambler
- const values = data.url.split('data=')[1].split('&')[0].split('.');
- ticket = values[0];
- scrambler = values[1];
- }
-
- let wnd = 'global';
- let location = data.url;
-
- // check if on same window or not
- if (data.url.indexOf('o_s_w=') !== -1) {
- const osw = /(.*)&o_s_w=.*/.exec(data.url);
- wnd = 'same';
- location = osw[1];
- //window.location.href = osw[1];
- } else {
- // If the url contains "o_n_w", will open the url on a new window ALWAYS
- if (data.url.indexOf('o_n_w=') !== -1) {
- // Extract window name from o_n_w parameter if present
- const onw = /(.*)&o_n_w=([a-zA-Z0-9._-]*)/.exec(
- data.url
- );
- if (onw) {
- wnd = onw[2];
- location = onw[1];
- }
- }
- }
-
- const openWindow = () => {
- if (wnd === 'same') {
- window.location.href = location;
- } else {
- if (Plugin.transportsWindow[wnd]) {
- Plugin.transportsWindow[wnd].close();
- }
- Plugin.transportsWindow[wnd] = window.open(
- data.url,
- 'uds_trans_' + wnd
- );
- }
- };
-
- // If credentials required, ask for them
- if (askCredentials) {
- this.api.gui
- .askCredentials(username, domain)
- .then((result) => {
- // Update transport credentials
- this.api.updateTransportTicket(ticket, scrambler,result.username, result.password, result.domain).then(
- () => {
- openWindow();
- }
- );
- });
- } else {
- openWindow(); // Open window
- }
- } else if (!data.running) {
- resolve(true);
- notifyError(data.error);
- } else {
- window.setTimeout(checker, this.delay); // Recheck after 5 seconds
- }
- },
- (error) => {
- resolve(true);
- notifyError(error);
- }
- );
- }
- };
- window.setTimeout(checker);
- })
- );
+ await this.processExternalUrl(url);
}
}
- private showAlert(
+ private async showAlert(
text: string,
info: string,
waitTime: number,
@@ -286,7 +48,7 @@ export class Plugin {
*
* @param url uds url to be lauhcned
*/
- private doLaunch(url: string) {
+ private launchUDSUrl(url: string) {
let elem: HTMLIFrameElement = document.getElementById(
'hiddenUdsLauncherIFrame'
) as HTMLIFrameElement;
@@ -302,4 +64,233 @@ export class Plugin {
}
elem.contentWindow.location.href = url;
}
+
+ /**
+ * Process an UDS url
+ *
+ * @param url uds url (udsa://serviceId/transportId)
+ * @returns nothing
+ */
+ private async processUDSUrl(url: string): Promise {
+ // Extract params from url, serviceId and transportId
+ const params = url.split('//')[1].split('/');
+ if (params.length !== 2) {
+ await this.notifyError(django.gettext('Invalid UDS URL'));
+ return;
+ }
+ const serviceId = params[0];
+ const transportId = params[1];
+
+ const dialog = await this.showAlert(
+ django.gettext('Please wait until the service is launched.'),
+ django.gettext(
+ 'Remember that you will need the UDS client on your platform to access the service.'
+ ),
+ 0
+ );
+ let cancel = false;
+
+ // Connect close dialog to "cancel" variable
+ toPromise(dialog.afterClosed()).then(() => (cancel = true));
+
+ let readyTime = -1;
+ try {
+ // Enable service
+ const enabledData = await this.api.enabler(serviceId, transportId);
+ if (enabledData.error) {
+ throw enabledData.error;
+ }
+ // Is HTTP access the service returned, or for UDS client?
+ if (enabledData.url.startsWith('/')) {
+ dialog.close();
+ await this.launchURL(enabledData.url);
+ return;
+ }
+ if (window.location.protocol === 'https:') {
+ // Ensures that protocol is https also for plugin, fixing if needed UDS provided info
+ enabledData.url = enabledData.url.replace('uds://', 'udss://');
+ }
+ // Launches UDS Client, using an iframe
+ await this.launchUDSUrl(enabledData.url);
+
+ while (!cancel) {
+ const data = await this.api.status(serviceId, transportId);
+ // Wait 5 times the default delay before notifying that client is not installed
+ if (readyTime > 0 && Date.now() - readyTime > this.delay * 5) {
+ dialog.componentInstance.data.title =
+ django.gettext('Service ready') +
+ ' - ' +
+ django.gettext('UDS Client not launching');
+ dialog.componentInstance.data.body =
+ '' +
+ django.gettext(
+ 'It seems that you don\'t have UDS Client installed. Please, install it from here:'
+ ) +
+ ' ' +
+ '' +
+ django.gettext('UDS Client Download') +
+ '';
+ }
+ if (data.status === 'ready') {
+ if (readyTime === -1) {
+ // Service is ready, wait for client, update dialog text
+ readyTime = Date.now(); // Milisecodns
+ dialog.componentInstance.data.title =
+ django.gettext('Service ready');
+ dialog.componentInstance.data.body = django.gettext(
+ 'Launching UDS Client, almost done.'
+ );
+ }
+ } else if (data.status === 'accessed') {
+ // stop checking
+ dialog.close();
+ cancel = true;
+ continue;
+ } else if (data.status !== 'running') {
+ // Service is not running, close dialog and notify error
+ dialog.close();
+ await this.notifyError(data.status);
+ cancel = true;
+ continue;
+ }
+ // Wait a second before checking again
+ await this.api.sleep(1000);
+ }
+ } catch (error) {
+ dialog.close();
+ await this.notifyError(error);
+ }
+ }
+
+ private async processExternalUrl(url: string): Promise {
+ const dialog = await this.showAlert(
+ django.gettext('Please wait until the service is launched.'),
+ django.gettext(
+ 'Remember that you will need the UDS client on your platform to access the service.'
+ ),
+ 0
+ );
+ let cancel = false;
+
+ // Connect close dialog to "cancel" variable
+ toPromise(dialog.afterClosed()).then(() => (cancel = true));
+ try {
+ while (!cancel) {
+ const data = await this.api.transportUrl(url);
+ if (data.url) {
+ dialog.close();
+
+ // if ask credentials, show dialog
+ // Extract username and domain "&creds=username@domain"
+ const creds = await this.processCredentials(data);
+ if (creds !== null) {
+ await this.api.updateTransportTicket(
+ creds.ticket,
+ creds.scrambler,
+ creds.username,
+ creds.password,
+ creds.domain
+ );
+ }
+ this.openWindow(data.url);
+ cancel = true;
+ } else {
+ if (!data.running) {
+ dialog.close();
+ await this.notifyError();
+ cancel = true;
+ }
+ }
+ }
+ // Wait a second before checking again
+ await this.api.sleep(1000);
+ } catch (error) {
+ dialog.close();
+ await this.notifyError(error);
+ }
+ }
+
+ private async processCredentials(
+ data: JSONTransportURLService
+ ): Promise {
+ if (data.url.indexOf('&creds=') !== -1) {
+ const creds = data.url.split('&creds=')[1];
+ let username = '';
+ let domain = '';
+ // Remove credentials from url
+ data.url = data.url.split('&creds=')[0];
+ // From "data=..." extract ticket and scrambler that is ticket.scrambler
+ const values = data.url.split('data=')[1].split('&')[0].split('.');
+ const ticket = values[0];
+ const scrambler = values[1];
+
+ if (creds.indexOf('@') !== -1) {
+ username = creds.split('@')[0];
+ domain = creds.split('@')[1];
+ } else {
+ username = creds;
+ }
+ const result = await this.api.gui.askCredentials(username, domain);
+ if (result.success === false) {
+ throw new Error('User canceled credentials dialog');
+ }
+ return {
+ ticket,
+ scrambler,
+ username: result.username,
+ password: result.password,
+ domain: result.domain,
+ };
+ }
+ return null;
+ }
+
+ private openWindow(location: string): void {
+ let wnd = 'global';
+ // check if on same window or not
+ if (location.indexOf('o_s_w=') !== -1) {
+ const osw = /(.*)&o_s_w=.*/.exec(location);
+ wnd = 'same';
+ location = osw[1];
+ } else {
+ // If the url contains "o_n_w", will open the url on a new window ALWAYS
+ if (location.indexOf('o_n_w=') !== -1) {
+ // Extract window name from o_n_w parameter if present
+ const onw = /(.*)&o_n_w=([a-zA-Z0-9._-]*)/.exec(location);
+ if (onw) {
+ wnd = onw[2];
+ location = onw[1];
+ }
+ }
+ }
+
+ if (wnd === 'same') {
+ window.location.href = location;
+ } else {
+ if (Plugin.transportsWindow[wnd]) {
+ Plugin.transportsWindow[wnd].close();
+ }
+ Plugin.transportsWindow[wnd] = window.open(location, 'uds_trans_' + wnd);
+ }
+ }
+
+ private async notifyError(error?: any): Promise {
+ let msg: string = django.gettext(
+ 'Error communicating with your service. Please, retry again.'
+ );
+ if (typeof error === 'string') {
+ msg = error;
+ } else if (error instanceof Error) {
+ msg = error.message;
+ } else if (error.status === 403) {
+ // Session timeout
+ msg = django.gettext('Your session has expired. Please, login again');
+ }
+ await this.showAlert(django.gettext('Error'), msg, 5000);
+ if (error.status === 403) {
+ this.api.logout();
+ }
+ }
}
diff --git a/src/app/pages/error/error.component.ts b/src/app/pages/error/error.component.ts
index 70f8262..ef9aab4 100644
--- a/src/app/pages/error/error.component.ts
+++ b/src/app/pages/error/error.component.ts
@@ -27,7 +27,7 @@ export class ErrorComponent implements OnInit {
}
this.error = '';
// Request error string from UDS
- this.api.getErrorInformation(id).subscribe((errInfo) => {
+ this.api.getErrorInformation(id).then((errInfo) => {
// Set error to errInfo.error + Hex code
this.error = errInfo.error;
});
diff --git a/src/app/pages/login/login.component.ts b/src/app/pages/login/login.component.ts
index 70ac36d..051e963 100644
--- a/src/app/pages/login/login.component.ts
+++ b/src/app/pages/login/login.component.ts
@@ -63,7 +63,7 @@ export class LoginComponent implements OnInit {
.setAttribute('style', 'display: none;');
this.api
.getAuthCustomHtml(l.id)
- .subscribe((result) => doCustomAuth(result));
+ .then((result) => doCustomAuth(result));
}
}
}
diff --git a/src/app/uds-api.service-type.ts b/src/app/uds-api.service-type.ts
index 6ed1248..f0388a5 100644
--- a/src/app/uds-api.service-type.ts
+++ b/src/app/uds-api.service-type.ts
@@ -42,6 +42,8 @@ export interface UDSApiServiceType {
/* Executes logout */
logout(): void;
+ /* sleep milliseconds */
+ sleep(ms: number): Promise;
/**
* Gets services information
*/
diff --git a/src/app/uds-api.service.ts b/src/app/uds-api.service.ts
index 55cd805..afaaaaa 100644
--- a/src/app/uds-api.service.ts
+++ b/src/app/uds-api.service.ts
@@ -94,7 +94,10 @@ export class UDSApiService implements UDSApiServiceType {
}
/* Client enabler */
- enabler(serviceId: string, transportId: string): Promise {
+ async enabler(
+ serviceId: string,
+ transportId: string
+ ): Promise {
const enabler = this.config.urls.enabler
.replace('param1', serviceId)
.replace('param2', transportId);
@@ -102,7 +105,10 @@ export class UDSApiService implements UDSApiServiceType {
}
/* Check userService status */
- status(serviceId: string, transportId: string): Promise {
+ async status(
+ serviceId: string,
+ transportId: string
+ ): Promise {
const status = this.config.urls.status
.replace('param1', serviceId)
.replace('param2', transportId);
@@ -110,18 +116,18 @@ export class UDSApiService implements UDSApiServiceType {
}
/* Services resetter */
- action(action: string, serviceId: string): Promise {
+ async action(action: string, serviceId: string): Promise {
const actionURL = this.config.urls.action
.replace('param1', serviceId)
.replace('param2', action);
return toPromise(this.http.get(actionURL));
}
- transportUrl(url: string): Promise {
+ async transportUrl(url: string): Promise {
return toPromise(this.http.get(url));
}
- updateTransportTicket(
+ async updateTransportTicket(
ticketId: string,
scrambler: string,
username: string,
@@ -160,16 +166,20 @@ export class UDSApiService implements UDSApiServiceType {
/**
* Gets services information
*/
- getServicesInformation(): Promise {
- return toPromise(this.http.get(this.config.urls.services));
+ async getServicesInformation(): Promise {
+ return toPromise(
+ this.http.get(this.config.urls.services)
+ );
}
/**
* Gets error string from a code
*/
- getErrorInformation(errorCode: string): Observable {
- return this.http.get(
- this.config.urls.error.replace('9999', errorCode)
+ async getErrorInformation(errorCode: string): Promise {
+ return toPromise(
+ this.http.get(
+ this.config.urls.error.replace('9999', errorCode)
+ )
);
}
@@ -193,6 +203,10 @@ export class UDSApiService implements UDSApiServiceType {
window.location.href = this.config.urls.logout;
}
+ sleep(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
launchURL(udsURL: string): void {
this.plugin.launchURL(udsURL);
}
@@ -203,10 +217,10 @@ export class UDSApiService implements UDSApiServiceType {
* @param authId if of the authenticator
* @returns Observable
*/
- getAuthCustomHtml(authId: string) {
- return this.http.get(this.config.urls.customAuth + authId, {
- responseType: 'text',
- });
+ async getAuthCustomHtml(authId: string): Promise {
+ return toPromise(
+ this.http.get(this.config.urls.customAuth + authId)
+ );
}
// Switch dark/light theme