import * as request from "request"; import * as Promise from "bluebird"; import * as _ from "lodash"; import { loadConfig } from "my-nconf"; import { InvalidArgumentError, ServiceUnavailableError } from "../errors"; import { Tools } from "../utils/Tools"; import { logger as defaultLogger } from "../utils/Logger"; export class WWW { private _tools: any = null; private _config: any = null; private _defaultOpts: any = null; private logger: any = null; private tracer: any = null; private static _instance; static get Instance() { if (!this._instance) { let c = loadConfig(); this._instance = new WWW({ webServiceTimeout: c.has("webServiceTimeout") ? c.get("webServiceTimeout") : 5000 }); } return this._instance; } constructor(_config, _logger = defaultLogger, _tracer = undefined) { this.logger = _logger; this.tracer = _tracer; this._tools = new Tools(); this._config = _.defaultsDeep({}, _config, { webServiceTimeout: 12 * 1000 //default 12 seconds }); this._defaultOpts = { proxy: undefined, time: false, timeout: this._config.webServiceTimeout, url: "http://localhost:80", //URL to hit qs: { from: "GetWebresponse", time: +new Date() }, //Query string data method: "POST", headers: { "Content-Type": "application/json" }, //default to JSON body: undefined }; } /** * [_requestHandler description] * @type {[type]} */ private _requestHandler = (resolve, reject) => { if (!resolve) throw new InvalidArgumentError("Missing args (resolve)"); if (!reject) throw new InvalidArgumentError("Missing args (Reject)"); const my = this; return (error, response, body) => { if (error) { return reject(new ServiceUnavailableError(error.message)); } my._tools .getJSONParser(response.headers["content-type"]) .then(parser => { return parser(body); }) .then(parsedData => { return resolve(parsedData); }) .catch(err => { my.logger.error(err); return resolve(body); //if failed to parse body, return as its for client to handle }); }; }; private convertID(id) { return id && typeof id == "string" ? id.replace(/-/g, "") : id; } private createSpan(ctx) { let my = this; my.logger.debug(ctx); // if (!my.tracer) return null; // const yo = ctx // ? new TraceId({ // traceId: new Some(my.convertID(ctx.requestID)), // parentId: new Some(my.convertID(ctx.id)), // spanId: my.tracer.createChildId().spanId.toString() //random 64 bits // }) // : my.tracer.createRootId(); // my.tracer.setId(yo); // return my.tracer.id; } private spanStart(id, opts, context) { let my = this; my.logger.debug(id, opts, context); // if (!my.tracer) return; // my.tracer.scoped(() => { // my.tracer.setId(id); // my.tracer.recordServiceName("WebHelper"); // my.tracer.recordRpc(opts.method.toUpperCase()); // my.tracer.recordBinary("http.url", opts.url); // my.tracer.recordAnnotation(new Annotation.ClientSend()); // // my.tracer.recordAnnotation(new Annotation.LocalAddr({port})); // }); } private spanEnd(error, id, opts, context) { let my = this; my.logger.debug(error, id, opts, context); // if (!my.tracer) return; // my.tracer.scoped(() => { // my.tracer.setId(id); // my.tracer.recordAnnotation(new Annotation.ClientRecv()); // }); } public wwwRequest(options, context = undefined, callback = undefined) { var my = this; const opts = _.defaultsDeep({}, options, my._defaultOpts); let id = my.createSpan(context); my.logger.debug(`SpanID: ${id}`); my.spanStart(id, opts, context); return new Promise(function(resolve, reject) { request(opts, my._requestHandler(resolve, reject)) .on("response", res => { my.logger.debug(res); my.spanEnd(null, id, opts, context); }) .on("error", err => { my.spanEnd(err, id, opts, context); }); }).nodeify(callback); //support callBack fallback } /** * shorthands & backwark compat */ public GetWebResponseWithHeaders( requestUrl, headers, requestBody, callBack = undefined, options = undefined, context = undefined, requestMethod = "POST" ) { var my = this; const defaults = { url: requestUrl, //URL to hit method: requestMethod, headers: _.defaultsDeep({}, headers, { "Content-Type": "application/x-www-form-urlencoded" }), //default to XML body body: requestBody }; return my.wwwRequest( _.defaultsDeep({}, options, defaults), context, callBack ); } public GetWebResponse( requestUrl, requestBody, callBack = undefined, options = undefined, context = undefined, requestMethod = "POST" ) { return this.GetWebResponseWithHeaders( requestUrl, null, requestBody, callBack, options, context, requestMethod ); } public GetWebResponseWithLicense( requestUrl, requestBody, license, callBack = undefined, options = undefined, context = undefined, requestMethod = "POST" ) { return this.GetWebResponseWithHeaders( requestUrl, { license: license }, requestBody, callBack, options, context, requestMethod ); } /** [GetWebResponseRaw description] */ public GetWebResponseRaw( requestUrl, requestBody, callBack = undefined, options = undefined, context = undefined, requestMethod = "POST" ) { return this.GetWebResponseWithHeaders( requestUrl, { "Content-Type": "application/raw" }, requestBody, callBack, options, context, requestMethod ); } /** * JSON to key=value string * @param {[type]} requestJson [description] * @param {[type]} delimiter [description] * @param {Boolean} hasHttp = true [description] */ public GetDelimitedRequestFromJSON(requestJson, delimiter, hasHttp = true) { let result: string[] = []; for (let key in requestJson) { let val = requestJson[key]; if (val === undefined) continue; result.push(`${key}"="${val}`); } if (hasHttp) { result.push("HTTP/1.1"); } return result.join(delimiter); } }