docker_images/node/wrapper/glue/glueUtils.js (121 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. 'use strict'; /*jshint esversion: 6 */ var respondWithCode = require('../utils/writer').respondWithCode; var debug = require('debug')('azure-iot-e2e:node') var Mqtt = require('azure-iot-device-mqtt').Mqtt; var MqttWs = require('azure-iot-device-mqtt').MqttWs; var Amqp = require('azure-iot-device-amqp').Amqp; var AmqpWs = require('azure-iot-device-amqp').AmqpWs; var Http = require('azure-iot-device-http').Http; const returnNotImpl = () => Promise.reject(respondWithCode(500, "function not implimented")); /** * return failure to the caller, passing the appropriate information back. * * @param {function} reject reject function from Promise * @param {Error} err error to return * @param {string} functionName name of function with failure */ var returnFailure = function(reject, err, functionName) { var errorText = ''; if (functionName) { errorText = 'failure from ' + functionName + ': '; } if (!err) { errorText = "unspecified failure"; } else if (err.responseBody) { errorText += err.responseBody; } else { errorText += err.message; } debug(`returning 500: ${errorText}`) reject(respondWithCode(500,errorText)); } /** * rethrow an error, wrapping it with a 500 return code * * @param {Error} err Error to rethrow * @param {string} functionName name of function with failure * @param {string} functionName name of function with failure */ var rethrowException = function(err, functionName) { var errorText = ''; if (functionName) { errorText = 'failure from ' + functionName + ': '; } errorText += err.message; debug(`rethrowing to return 500: ${errorText}`) throw(respondWithCode(500,errorText)) } /** * print details of function call to debug log * * @param {string} functionName name of function that was just called. * @param {Error} err result of the function call. */ var debugFunctionResult = function(functionName, err) { debug(`${functionName} returned ${err ? err.message : 'success'}`); } /** * Make a promise that can be used inside of an API handler * * @param {string} functionName Name of the function, used to return error strings * @param {function} func Function that is being wrapped/called * * @returns Promise that wraps the passed function */ var makePromise = function(functionName, func) { return new Promise(function(resolve, reject) { try { func(function(err, result) { debugFunctionResult(functionName, err); if (err) { returnFailure(reject, err, functionName); } else { if (result) { if (typeof(result) === 'object') { try { debug(`result of ${functionName}: ${JSON.stringify(result)}`); } catch (e) { debug(`can\'t print result object: ${e.message}`); } } else { debug(`result: ${result}`); } } resolve(result); } }); } catch (e) { rethrowException(e, functionName); } }); } /** * Given a transport name, return the constructor for that transport. * * @param {string} transport: one of mqtt, mqttws, amqp, amqpws, or http * * @returns Transport constructor */ var transportFromType = function(transport) { switch (transport.toLowerCase()) { case 'mqtt': return Mqtt; case 'mqttws': return MqttWs; case 'amqp': return Amqp; case 'amqpws': return AmqpWs; case 'http': return Http; default: return null; } } /** * Set a cert on a client. If the cert is not provided, the callback is called immediately. * * @param {*} client Client object to set the certificate on * @param {*} cert Certificate to set * @param {*} done callback to call after the cert is set */ var setOptionalCert = function(client, cert, done) { if (!cert || !cert.cert) { done(); } else { client.setOptions({ ca: cert.cert }, done); } } /** * Replace exports from one file with functions exported from another file. We use this function * to make it easier to re-generate the stub code. With this, we can replace the auto-generated * functions in the service directory with their equivalent functions in the glue folder. * * Here's how it works: * * service/ModuleService.js (etc) has a bunch of handler functions that are auto-generated for us. * The intention of this file is that app developers would replace the implementations inside this * file with "real" code to handle the various rest api calls. * * However, if app developers manually insert their code into one of these services files, then * they're in for a world of pain the next time they need to run the codegen tools because they * have to merge the newly generated code with their manual insertions. This is an painful and * error-prone operation. * * To get around this pain, we use this function that replaces, at runtime, all of the functions * exported from service/moduleService.js (etc) with functions from ModuleGlue.js. This way, the * only "merging" that app developers need to do is to make sure that this function gets called * at the end of ModuleService.js. If any functions are added or removed from ModuleService.js, * this code will be able to notice this and flag an error. * */ var replaceExports = function(oldExports, filename) { var fail = false; var exports = {}; // Load the glue module var thisMod = require('./' + filename); // For each export from the glue module, copy it into our exports. Make sure we're replacing a previous one instead of adding a new one. Object.keys(thisMod).forEach(function(exportName) { if (!oldExports[exportName]) { if (!exportName.startsWith('_')) { console.log(`warning: export ${exportName} found in ${filename} but not in ${__dirname}. This may be in multiple glue files or it may have been renamed in a newer swagger file`); fail = true; } } else { delete oldExports[exportName]; exports[exportName] = thisMod[exportName]; } }); // Now go through our old exports and make sure we've replaced everything. Object.keys(oldExports).forEach(function(exportName) { console.log(`warning: export ${exportName} was not replaced. Using codegen stub`); fail = true; }); if (fail) { throw new Error(`failed replacing codegen stubs with implementations. Open a node prompt and import your XxxxService.js file to see details.`) } return exports; } module.exports = { returnNotImpl: returnNotImpl, rethrowException: rethrowException, debugFunctionResult: debugFunctionResult, makePromise: makePromise, transportFromType: transportFromType, setOptionalCert: setOptionalCert, replaceExports: replaceExports }