UNPKG

domotz-remote-pawn

Version:

Domotz Agent

226 lines (193 loc) 7.97 kB
/** This file is part of Domotz Agent. * Copyright (C) 2016 Domotz Ltd * * Domotz Agent is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Domotz Agent is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>. * * Created by Tommaso Latini <tommaso@domotz.com> on 28/08/15. */ const MODULE_NAME = 'API_CLIENT'; const PORTAL_API_ENDPOINT = 'https://portal.domotz.com/portal-api/v1/'; const ENDPOINT_SPECIFIER_HEADER = 'x-api-endpoint'; const DEFAULT_REQUEST_TIMEOUT = 30000; const DEFAULT_MAX_ATTEMPTS = 0; const DEFAULT_RETRY_DELAY = 0; /** * Wrapper of NODE request. * It provides utilities like: * - Flexible API endpoint * - Standard callback for error and response handling * - Wrapper for promise * @param q * @param request * @param conf * @param t * @returns {{ * setCredentials: setCredentials, * send: send, * sendWithPromise: sendWithPromise, * getBaseURL: Function * }} */ var factory = function (q, request, conf, t) { /** * PORTAL_API_ENDPOINT should be an environment variable * only in development mode */ var type = t; var baseUrl = process.env.PORTAL_API_ENDPOINT || PORTAL_API_ENDPOINT; var credentials = null; function setCredentials(user, password) { credentials = {user: user, password: password}; } function getUrl(doNotAddBaseUrl, resource) { return doNotAddBaseUrl ? resource : baseUrl + resource; } function getMethod(method) { return request.METHOD_TO_REQUEST_MAP[method]; } function getBaseURL() { return baseUrl; } function getOptions(url, body, options) { options = options || {}; options.url = url; options.json = body; options.auth = credentials; options.timeout = options.timeout || DEFAULT_REQUEST_TIMEOUT; options.maxAttempts = options.maxAttempts || DEFAULT_MAX_ATTEMPTS; options.retryDelay = options.retryDelay || DEFAULT_RETRY_DELAY; return options; } function logRequest(options, verb, url, body) { var stringBody = JSON.stringify(body) || ''; console.info('%s - (%s): %s request to %s endpoint: %s. Body: %d bytes%s', MODULE_NAME, type, verb, type, url, stringBody.length, options ? '. Options: ' + JSON.stringify(options, hidePassword) : ''); console.debug('%s - (%s): Request Body: %s', MODULE_NAME, type, stringBody); } function responseHandler(url, verb, response, body, customHandler, doNotAddBaseUrl) { if (response.attempts && response.attempts > 1) { console.debug("Response received after " + response.attempts + " attempts."); } console.debug('%s - (%s): Engine request OK! Requested URL: <%s> ' + 'Method: %s - Status Code: %d', MODULE_NAME, (doNotAddBaseUrl ? 'custom' : type), url, verb, response.statusCode); console.verbose("%s - Response Body: %s", MODULE_NAME, body); if (customHandler !== undefined) { customHandler(response, body); } } function errorHandler(url, verb, error, customHandler, doNotAddBaseUrl) { console.warn('%s - (%s): Engine request error! Requested URL: <%s> Method: %s - ' + 'Error code: %s. %s => %s', MODULE_NAME, (doNotAddBaseUrl ? 'custom' : type), url, verb, (error.status || 'No HTTP error'), error.status ? "Response Body" : "Error message", JSON.stringify(error.message)); error.stack = error.stack || new Error(error).stack; console.warn(error.stack); if (customHandler !== undefined) { customHandler(error); } } function send(resource, verb, body, onSuccess, onError, doNotAddBaseUrl, otherOptions) { var url = getUrl(doNotAddBaseUrl, resource); var method = getMethod(verb); var options = getOptions(url, body, otherOptions); logRequest(otherOptions, verb, url, body); request[method](options, function callback(error, response, body) { try { if (error) { errorHandler(url, verb, error, onError, doNotAddBaseUrl); return; } var statusCode = response.statusCode; if (statusCode > 299 || statusCode < 200) { error = { message: body, status: statusCode }; errorHandler(url, verb, error, onError, doNotAddBaseUrl); return; } var location = response.headers.location; if (statusCode === 204 && location) { send(location, 'GET', null, onSuccess, onError, true); return; } var xApiEndpointHeader = response.headers[ENDPOINT_SPECIFIER_HEADER]; if (xApiEndpointHeader) { console.info("%s - (%s): Switching baseURL to: %s", MODULE_NAME, type, xApiEndpointHeader); baseUrl = xApiEndpointHeader; } responseHandler(url, verb, response, body, onSuccess, doNotAddBaseUrl); } catch (exception) { errorHandler(url, verb, exception, onError); } }); } function sendWithPromise(resource, method, body, handleResponse, doNotAddBaseUrl, otherOptions) { var deferred = q.defer(); function parseSuccessfulResponse(response, body) { if (typeof handleResponse === 'function') { deferred.resolve(handleResponse(response)); } else if (typeof body === 'string') { deferred.resolve(JSON.parse(body)); } else { deferred.resolve(body); } } function parseError(error) { deferred.reject(error); } send(resource, method, body, parseSuccessfulResponse, parseError, doNotAddBaseUrl, otherOptions); return deferred.promise; } switch (type) { case 'register': console.verbose('Register API initialization'); break; case 'agent': console.verbose('Agent API initialization'); baseUrl = conf.engine.api_endpoints.hub + 'agent/' + conf.id; credentials = conf.credentials; break; case 'agent-heartbeat': console.verbose('Agent-Heartbeat API initialization'); baseUrl = conf.engine.api_endpoints.hub + 'agent/' + conf.credentials.user; credentials = conf.credentials; break; case 'user': console.verbose('User API initialization'); baseUrl = conf ? conf.engine.api_endpoints.app : baseUrl; break; case 'other': break; // Neither baseUrl nor credentials default: throw new Error("Wrong type for engine client"); } console.info('%s - (%s): Create api client. Endpoint: %s.', MODULE_NAME, type, baseUrl); return { getBaseURL: getBaseURL, setCredentials: setCredentials, send: send, sendWithPromise: sendWithPromise }; }; module.exports.factory = factory; function hidePassword(key, value) { if (key === 'password' || key === 'pass') { return '*********'; } else { return value; } }