UNPKG

pip-services3-rpc-node

Version:
370 lines 15.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RestClient = void 0; /** @module clients */ /** @hidden */ let _ = require('lodash'); /** @hidden */ let querystring = require('querystring'); const pip_services3_commons_node_1 = require("pip-services3-commons-node"); const pip_services3_components_node_1 = require("pip-services3-components-node"); const pip_services3_components_node_2 = require("pip-services3-components-node"); const pip_services3_commons_node_2 = require("pip-services3-commons-node"); const pip_services3_commons_node_3 = require("pip-services3-commons-node"); const pip_services3_commons_node_4 = require("pip-services3-commons-node"); const HttpConnectionResolver_1 = require("../connect/HttpConnectionResolver"); /** * Abstract client that calls remove endpoints using HTTP/REST protocol. * * ### Configuration parameters ### * * - base_route: base route for remote URI * - connection(s): * - discovery_key: (optional) a key to retrieve the connection from [[https://pip-services3-node.github.io/pip-services3-components-node/interfaces/connect.idiscovery.html IDiscovery]] * - protocol: connection protocol: http or https * - host: host name or IP address * - port: port number * - uri: resource URI or connection string with all parameters in it * - options: * - retries: number of retries (default: 3) * - connect_timeout: connection timeout in milliseconds (default: 10 sec) * - timeout: invocation timeout in milliseconds (default: 10 sec) * - correlation_id_place place for adding correalationId, query - in query string, headers - in headers, both - in query and headers (default: query) * * ### References ### * * - <code>\*:logger:\*:\*:1.0</code> (optional) [[https://pip-services3-node.github.io/pip-services3-components-node/interfaces/log.ilogger.html ILogger]] components to pass log messages * - <code>\*:counters:\*:\*:1.0</code> (optional) [[https://pip-services3-node.github.io/pip-services3-components-node/interfaces/count.icounters.html ICounters]] components to pass collected measurements * - <code>\*:discovery:\*:\*:1.0</code> (optional) [[https://pip-services3-node.github.io/pip-services3-components-node/interfaces/connect.idiscovery.html IDiscovery]] services to resolve connection * * @see [[RestService]] * @see [[CommandableHttpService]] * * ### Example ### * * class MyRestClient extends RestClient implements IMyClient { * ... * * public getData(correlationId: string, id: string, * callback: (err: any, result: MyData) => void): void { * * let timing = this.instrument(correlationId, 'myclient.get_data'); * this.call("get", "/get_data" correlationId, { id: id }, null, (err, result) => { * timing.endTiming(); * callback(err, result); * }); * } * ... * } * * let client = new MyRestClient(); * client.configure(ConfigParams.fromTuples( * "connection.protocol", "http", * "connection.host", "localhost", * "connection.port", 8080 * )); * * client.getData("123", "1", (err, result) => { * ... * }); */ class RestClient { constructor() { /** * The connection resolver. */ this._connectionResolver = new HttpConnectionResolver_1.HttpConnectionResolver(); /** * The logger. */ this._logger = new pip_services3_components_node_1.CompositeLogger(); /** * The performance counters. */ this._counters = new pip_services3_components_node_2.CompositeCounters(); /** * The configuration options. */ this._options = new pip_services3_commons_node_1.ConfigParams(); /** * The number of retries. */ this._retries = 1; /** * The default headers to be added to every request. */ this._headers = {}; /** * The connection timeout in milliseconds. */ this._connectTimeout = 10000; /** * The invocation timeout in milliseconds. */ this._timeout = 10000; this._correlationIdPlace = "query"; } /** * Configures component by passing configuration parameters. * * @param config configuration parameters to be set. */ configure(config) { config = config.setDefaults(RestClient._defaultConfig); this._connectionResolver.configure(config); this._options = this._options.override(config.getSection("options")); this._retries = config.getAsIntegerWithDefault("options.retries", this._retries); this._connectTimeout = config.getAsIntegerWithDefault("options.connect_timeout", this._connectTimeout); this._timeout = config.getAsIntegerWithDefault("options.timeout", this._timeout); this._baseRoute = config.getAsStringWithDefault("base_route", this._baseRoute); this._correlationIdPlace = config.getAsStringWithDefault("options.correlation_id_place", this._correlationIdPlace); } /** * Sets references to dependent components. * * @param references references to locate the component dependencies. */ setReferences(references) { this._logger.setReferences(references); this._counters.setReferences(references); this._connectionResolver.setReferences(references); } /** * Adds instrumentation to log calls and measure call time. * It returns a CounterTiming object that is used to end the time measurement. * * @param correlationId (optional) transaction id to trace execution through call chain. * @param name a method name. * @returns CounterTiming object to end the time measurement. */ instrument(correlationId, name) { const typeName = this.constructor.name || "unknown-target"; this._logger.trace(correlationId, "Calling %s method of %s", name, typeName); this._counters.incrementOne(typeName + "." + name + '.call_count'); return this._counters.beginTiming(typeName + "." + name + ".call_time"); } /** * Adds instrumentation to error handling. * * @param correlationId (optional) transaction id to trace execution through call chain. * @param name a method name. * @param err an occured error * @param result (optional) an execution result * @param callback (optional) an execution callback */ instrumentError(correlationId, name, err, result = null, callback = null) { if (err != null) { const typeName = this.constructor.name || "unknown-target"; this._logger.error(correlationId, err, "Failed to call %s method of %s", name, typeName); this._counters.incrementOne(typeName + "." + name + '.call_errors'); } if (callback) callback(err, result); } /** * Checks if the component is opened. * * @returns true if the component has been opened and false otherwise. */ isOpen() { return this._client != null; } /** * Opens the component. * * @param correlationId (optional) transaction id to trace execution through call chain. * @param callback callback function that receives error or null no errors occured. */ open(correlationId, callback) { if (this.isOpen()) { if (callback) callback(null); return; } this._connectionResolver.resolve(correlationId, (err, connection) => { if (err) { if (callback) callback(err); return; } try { this._uri = connection.getUri(); let restify = require('restify-clients'); this._client = restify.createJsonClient({ url: this._uri, connectTimeout: this._connectTimeout, requestTimeout: this._timeout, headers: this._headers, retry: { minTimeout: this._timeout, maxTimeout: Infinity, retries: this._retries }, version: '*' }); this._logger.debug(correlationId, "Connected via REST to %s", this._uri); if (callback) callback(null); } catch (err) { this._client = null; let ex = new pip_services3_commons_node_3.ConnectionException(correlationId, "CANNOT_CONNECT", "Connection to REST service failed") .wrap(err).withDetails("url", this._uri); if (callback) callback(ex); } }); } /** * Closes component and frees used resources. * * @param correlationId (optional) transaction id to trace execution through call chain. * @param callback callback function that receives error or null no errors occured. */ close(correlationId, callback) { if (this._client != null) { // Eat exceptions try { this._logger.debug(correlationId, "Closed REST service at %s", this._uri); } catch (ex) { this._logger.warn(correlationId, "Failed while closing REST service: %s", ex); } this._client = null; this._uri = null; } if (callback) callback(null); } /** * Adds a correlation id (correlation_id) to invocation parameter map. * * @param params invocation parameters. * @param correlationId (optional) a correlation id to be added. * @returns invocation parameters with added correlation id. */ addCorrelationId(params, correlationId) { // Automatically generate short ids for now if (correlationId == null) //correlationId = IdGenerator.nextShort(); return params; params = params || {}; params.correlation_id = correlationId; return params; } /** * Adds filter parameters (with the same name as they defined) * to invocation parameter map. * * @param params invocation parameters. * @param filter (optional) filter parameters * @returns invocation parameters with added filter parameters. */ addFilterParams(params, filter) { params = params || {}; if (filter) { for (let prop in filter) { if (filter.hasOwnProperty(prop)) params[prop] = filter[prop]; } } return params; } /** * Adds paging parameters (skip, take, total) to invocation parameter map. * * @param params invocation parameters. * @param paging (optional) paging parameters * @returns invocation parameters with added paging parameters. */ addPagingParams(params, paging) { params = params || {}; if (paging) { if (paging.total) params.total = paging.total; if (paging.skip) params.skip = paging.skip; if (paging.take) params.take = paging.take; } return params; } createRequestRoute(route) { let builder = ""; if (this._baseRoute != null && this._baseRoute.length > 0) { if (this._baseRoute[0] != "/") builder += "/"; builder += this._baseRoute; } if (route.length != 0 && route[0] != "/") builder += "/"; builder += route; return builder; } /** * Calls a remote method via HTTP/REST protocol. * * @param method HTTP method: "get", "head", "post", "put", "delete" * @param route a command route. Base route will be added to this route * @param correlationId (optional) transaction id to trace execution through call chain. * @param params (optional) query parameters. * @param data (optional) body object. * @param callback (optional) callback function that receives result object or error. */ call(method, route, correlationId, params = {}, data, callback) { method = method.toLowerCase(); if (_.isFunction(data)) { callback = data; data = {}; } route = this.createRequestRoute(route); if (this._correlationIdPlace == "query" || this._correlationIdPlace == "both") { params = this.addCorrelationId(params, correlationId); } if (this._correlationIdPlace == "headers" || this._correlationIdPlace == "both") { this._headers['correlation_id'] = correlationId; } if (!_.isEmpty(params)) route += '?' + querystring.stringify(params); let self = this; let action = null; if (callback) { action = (err, req, res, data) => { // Handling 204 codes if (res && res.statusCode == 204) callback.call(self, null, null); else if (err == null) callback.call(self, null, data); else { // Restore application exception if (data != null) err = pip_services3_commons_node_2.ApplicationExceptionFactory.create(data).withCause(err); callback.call(self, err, null); } }; } if (method == 'get') this._client.get(route, action); else if (method == 'head') this._client.head(route, action); else if (method == 'post') this._client.post(route, data, action); else if (method == 'put') this._client.put(route, data, action); else if (method == 'patch') this._client.patch(route, data, action); else if (method == 'delete') this._client.del(route, action); else { let error = new pip_services3_commons_node_4.UnknownException(correlationId, 'UNSUPPORTED_METHOD', 'Method is not supported by REST client') .withDetails('verb', method); if (callback) callback(error, null); else throw error; } } } exports.RestClient = RestClient; RestClient._defaultConfig = pip_services3_commons_node_1.ConfigParams.fromTuples("connection.protocol", "http", "connection.host", "0.0.0.0", "connection.port", 3000, "options.request_max_size", 1024 * 1024, "options.connect_timeout", 10000, "options.timeout", 10000, "options.retries", 3, "options.debug", true, "options.correlation_id_place", "query"); //# sourceMappingURL=RestClient.js.map