Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Add getSpinner method to $progressIndicator #1040

Merged
merged 2 commits into from
Jan 23, 2018
Merged
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
32 changes: 32 additions & 0 deletions declarations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,38 @@ interface IProgressIndicator {
* @return {Promise<T>}
*/
showProgressIndicator<T>(promise: Promise<T>, timeout: number, options?: { surpressTrailingNewLine?: boolean }): Promise<T>;

/**
* Returns a spinner instance that will print a specified message when spinner is started and will repeat it until spinner is stopped.
* In case the terminal is not interactive, a mocked instance is returned, so the spinner will print the required message a single time - when it is started.
* @param {string} message The message to be printed.
* @returns {ISpinner} Instance of clui.Spinner in case terminal is interactive, mocked instance otherwise.
*/
getSpinner(message: string): ISpinner;
}

/**
* Describes the clui spinner.
*/
interface ISpinner {
/**
* Sets the message that will be printed by spinner.
* @param {string} msg The new message.
* @returns {void}
*/
message(msg: string): void;

/**
* Starts the spinner.
* @returns {void}
*/
start(): void;

/**
* Stops the spinner.
* @returns {void}
*/
stop(): void;
}

/**
Expand Down
44 changes: 44 additions & 0 deletions helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,53 @@ import { ReadStream } from "tty";
import { Configurations } from "./constants";
import { EventEmitter } from "events";
import * as crypto from "crypto";
import progress = require("progress-stream");
import filesize = require("filesize");
import * as util from "util";

const Table = require("cli-table");

export function trackDownloadProgress(destinationStream: NodeJS.WritableStream, url: string): NodeJS.ReadableStream {
// \r for carriage return doesn't work on windows in node for some reason so we have to use it's hex representation \x1B[0G
let lastMessageSize = 0;
const carriageReturn = "\x1B[0G";
let timeElapsed = 0;

const isInteractiveTerminal = isInteractive();
const progressStream = progress({ time: 1000 }, (progress: any) => {
timeElapsed = progress.runtime;

if (timeElapsed >= 1) {
if (isInteractiveTerminal) {
this.$logger.write("%s%s", carriageReturn, Array(lastMessageSize + 1).join(" "));

const message = util.format("%sDownload progress ... %s | %s | %s/s",
carriageReturn,
Math.floor(progress.percentage) + "%",
filesize(progress.transferred),
filesize(progress.speed));

this.$logger.write(message);
lastMessageSize = message.length;
}
}
});

progressStream.on("finish", () => {
if (timeElapsed >= 1) {
const msg = `Download of ${url} completed.`;
if (isInteractiveTerminal) {
this.$logger.out("%s%s%s%s", carriageReturn, Array(lastMessageSize + 1).join(" "), carriageReturn, msg);
} else {
this.$logger.out(msg);
}
}
});

progressStream.pipe(destinationStream);
return progressStream;
}

export async function executeActionByChunks<T>(initialData: T[] | IDictionary<T>, chunkSize: number, elementAction: (element: T, key?: string | number) => Promise<any>): Promise<void> {
let arrayToChunk: (T | string)[];
let action: (key: string | T) => Promise<any>;
Expand Down
37 changes: 0 additions & 37 deletions http-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { EOL } from "os";
import * as helpers from "./helpers";
import * as zlib from "zlib";
import * as util from "util";
import progress = require("progress-stream");
import filesize = require("filesize");
import { HttpStatusCodes } from "./constants";
import * as request from "request";

Expand Down Expand Up @@ -133,8 +131,6 @@ export class HttpClient implements Server.IHttpClient {
this.setResponseResult(promiseActions, timerId, { response });
});

pipeTo = this.trackDownloadProgress(pipeTo);

responseStream.pipe(pipeTo);
} else {
const data: string[] = [];
Expand Down Expand Up @@ -204,39 +200,6 @@ export class HttpClient implements Server.IHttpClient {
}
}

private trackDownloadProgress(pipeTo: NodeJS.WritableStream): NodeJS.ReadableStream {
// \r for carriage return doesn't work on windows in node for some reason so we have to use it's hex representation \x1B[0G
let lastMessageSize = 0;
const carriageReturn = "\x1B[0G";
let timeElapsed = 0;

const progressStream = progress({ time: 1000 }, (progress: any) => {
timeElapsed = progress.runtime;

if (timeElapsed >= 1) {
this.$logger.write("%s%s", carriageReturn, Array(lastMessageSize + 1).join(" "));

const message = util.format("%sDownload progress ... %s | %s | %s/s",
carriageReturn,
Math.floor(progress.percentage) + "%",
filesize(progress.transferred),
filesize(progress.speed));

this.$logger.write(message);
lastMessageSize = message.length;
}
});

progressStream.on("finish", () => {
if (timeElapsed >= 1) {
this.$logger.out("%s%s%s%s", carriageReturn, Array(lastMessageSize + 1).join(" "), carriageReturn, "Download completed.");
}
});

progressStream.pipe(pipeTo);
return progressStream;
}

private getErrorMessage(statusCode: number, body: string): string {
if (statusCode === HttpStatusCodes.PROXY_AUTHENTICATION_REQUIRED) {
const clientNameLowerCase = this.$staticConfig.CLIENT_NAME.toLowerCase();
Expand Down
29 changes: 24 additions & 5 deletions progress-indicator.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import { isInteractive } from './helpers';

const clui = require("clui");

export class ProgressIndicator implements IProgressIndicator {
constructor(private $logger: ILogger) { }

public async showProgressIndicator<T>(promise: Promise<T>, timeout: number, options?: { surpressTrailingNewLine?: boolean }): Promise<T> {
const surpressTrailingNewLine = options && options.surpressTrailingNewLine;

let isResolved = false;
let isFulfilled = false;

const tempPromise = new Promise<T>((resolve, reject) => {
promise.then((res) => {
isResolved = true;
isFulfilled = true;
resolve(res);
}, (err) => {
isResolved = true;
isFulfilled = true;
reject(err);
});
});

while (!isResolved) {
await this.$logger.printMsgWithTimeout(".", timeout);
if (!isInteractive()) {
while (!isFulfilled) {
await this.$logger.printMsgWithTimeout(".", timeout);
}
}

if (!surpressTrailingNewLine) {
Expand All @@ -26,5 +32,18 @@ export class ProgressIndicator implements IProgressIndicator {

return tempPromise;
}

public getSpinner(message: string): ISpinner {
if (isInteractive()) {
return new clui.Spinner(message);
} else {
let msg = message;
return {
start: () => this.$logger.info(msg),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For cloud builds this will print for example Installing tns-android and we are going to loose the broken "progress indicator" which we currently have. Without it our users may wonder is the build slow or is there some other problem (network connectivity issue on their side, or some bug on our side).
Do you think it will be better to have some kind of progress indicator instead of just one message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't rely on the cloud build logs in order to show progress indication on the client side. IMO this should be handled on the client side in any other meaningful way.
@tsvetie what do you think?

message: (newMsg: string) => msg = newMsg,
stop: (): void => undefined
};
}
}
}
$injector.register("progressIndicator", ProgressIndicator);