From 92c783ef3f46db145118d619cae18cea794aa374 Mon Sep 17 00:00:00 2001 From: balihoo-rjgupta Date: Mon, 13 Feb 2023 11:01:54 +0530 Subject: [PATCH 1/2] downgrade version of restify from 5.0.1 to 4.3.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c488e2f..533f1fd 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "jsonstream2": "3.0.0", "bluebird": "3.5.0", "bunyan": "1.8.12", - "restify": "5.0.1", + "restify": "4.3.4", "restify-errors": "4.3.0", "restify-plugins": "1.6.0", "swagger-tools": "0.10.1", From 286115939a3a37cac8d49e95f7dc7414a32f4021 Mon Sep 17 00:00:00 2001 From: balihoo-rjgupta Date: Mon, 13 Feb 2023 11:51:33 +0530 Subject: [PATCH 2/2] added lib folder to repository --- lib/error.js | 36 ++++++ lib/logger.js | 83 +++++++++++++ lib/middleware/swaggerParameterReplacer.js | 16 +++ lib/responder.js | 82 +++++++++++++ lib/response.js | 24 ++++ lib/router.js | 95 +++++++++++++++ lib/server.js | 129 +++++++++++++++++++++ 7 files changed, 465 insertions(+) create mode 100644 lib/error.js create mode 100644 lib/logger.js create mode 100644 lib/middleware/swaggerParameterReplacer.js create mode 100644 lib/responder.js create mode 100644 lib/response.js create mode 100644 lib/router.js create mode 100644 lib/server.js diff --git a/lib/error.js b/lib/error.js new file mode 100644 index 0000000..87c4058 --- /dev/null +++ b/lib/error.js @@ -0,0 +1,36 @@ +// Generated by CoffeeScript 2.5.1 +'use strict'; +var MissingApiConfigError, MissingRouteHandlerError, MissingRouteHandlersConfigError; + +exports.MissingApiConfigError = MissingApiConfigError = function() { + var self; + self = new Error("Required parameter 'api' not specified. Must be an object or the path to your API documentation."); + self.name = 'MissingApiConfig'; + self.__proto__ = MissingApiConfigError.prototype; + return self; +}; + +MissingApiConfigError.prototype.__proto__ = Error.prototype; + +exports.MissingRouteHandlersConfigError = MissingRouteHandlersConfigError = function() { + var self; + self = new Error("Required parameter 'routeHandlers' not specified. Must be an object or the path to a module."); + self.name = 'MissingRouteHandlersConfig'; + self.__proto__ = MissingRouteHandlersConfigError.prototype; + return self; +}; + +MissingRouteHandlersConfigError.prototype.__proto__ = Error.prototype; + +exports.MissingRouteHandlerError = MissingRouteHandlerError = function(operation) { + var self; + self = new Error(`No handler found for ${operation.method} ${operation.path}, operation ID ${operation.operationId}.`); + self.name = 'MissingRouteHandler'; + self.path = operation.path; + self.method = operation.method; + self.operationId = operation.operationId; + self.__proto__ = MissingRouteHandlerError.prototype; + return self; +}; + +MissingRouteHandlerError.prototype.__proto__ = Error.prototype; diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..1a557a4 --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,83 @@ +// Generated by CoffeeScript 2.5.1 +'use strict'; +/* + Define a special toJSON property for serializing Error objects + Allows logging of custom properties including nested Error objects +*/ +var Bunyan, Logger, errorToJson; + +Bunyan = require('bunyan'); + +errorToJson = { + configurable: true, + value: function() { + var alt, storeKey; + alt = {}; + storeKey = function(key) { + return alt[key] = this[key]; + }; + Object.getOwnPropertyNames(this).forEach(storeKey, this); + return alt; + } +}; + +Object.defineProperty(Error.prototype, 'toJSON', errorToJson); + +module.exports = Logger = class Logger { + constructor(opts) { + var logConfig; + opts.log = opts.log || {}; + if (opts.log.logger) { + // User provided their own logger + this.log = opts.log.logger; + } else { + // Use bunyan for logging + logConfig = {}; + logConfig.name = opts.name || 'snapi'; + logConfig.streams = opts.log.streams || [ + { + path: opts.log.path || './snapi.log', + type: opts.log.type || 'rotating-file', + level: opts.log.level || 'warn', + period: opts.log.period || '1d', + count: opts.log.count || 10 + } + ]; + this.log = new Bunyan(logConfig); + } + } + + unhandledRejection() { + return (err) => { + return this.log.error({ + unhandledRejection: true, + err: err + }, err.message); + }; + } + + unhandledProcessException() { + return (err) => { + return this.log.fatal({ + unhandledProcessException: true, + err: err + }, err.message); + }; + } + + unhandledRestifyException() { + return (req, res, route, err) => { + this.log.error({ + unhandledRestifyException: true, + req: req, + res: res, + route: route, + err: err + }, err.message); + if (!res.headersSent) { + return res.send(500, 'Internal server error'); + } + }; + } + +}; diff --git a/lib/middleware/swaggerParameterReplacer.js b/lib/middleware/swaggerParameterReplacer.js new file mode 100644 index 0000000..ddd1cad --- /dev/null +++ b/lib/middleware/swaggerParameterReplacer.js @@ -0,0 +1,16 @@ +// Generated by CoffeeScript 2.5.1 +module.exports = function() { + return function(request, response, next) { + var param, paramName, ref, ref1; + if (request != null ? (ref = request.swagger) != null ? ref.params : void 0 : void 0) { + ref1 = request.swagger.params; + for (paramName in ref1) { + param = ref1[paramName]; + if (request.params[paramName] != null) { + param.value = request.params[paramName]; + } + } + } + return next(); + }; +}; diff --git a/lib/responder.js b/lib/responder.js new file mode 100644 index 0000000..20aaab3 --- /dev/null +++ b/lib/responder.js @@ -0,0 +1,82 @@ +// Generated by CoffeeScript 2.5.1 +'use strict'; +var Promise, Responder, Response, codes, errors, jsonStream, stream; + +Promise = require('bluebird'); + +errors = require('restify-errors'); + +stream = require('stream'); + +jsonStream = require('jsonstream2'); + +Response = require('./response').Response; + +codes = { + successWithBody: 200, + successNoBody: 204, + movedPermanently: 301, + found: 302, + internalError: 500, + invalidCredentials: 401, + notAuthorized: 403 +}; + +module.exports = Responder = (function() { + class Responder { + constructor(logger) { + this.logger = logger; + } + + errorResponse(err, response, next) { + this.logger.log.error(err); + if (err instanceof errors.InvalidCredentialsError) { + response.header('Www-Authenticate', 'Basic'); + } + return next(err); + } + + respondStream(result, response) { + var ref; + response.writeHead(200, { + 'Content-Type': 'application/json; charset=utf-8' + }); + if (result.objectMode || ((ref = result._readableState) != null ? ref.objectMode : void 0)) { + return result.pipe(jsonStream.stringify()).pipe(response); + } else { + return result.pipe(response); + } + } + + respond(result, response, next) { + var body, header, ref, statusCode, value; + if (result instanceof Error) { + return this.errorResponse(result, response, next); + } + if (result instanceof stream.Stream) { + return this.respondStream(result, response); + } + // Send the result as the body and 200 or 204 based on result being defined and non-null + body = result; + statusCode = result != null ? codes.successWithBody : codes.successNoBody; + if (result instanceof Response) { + ref = result.headers; + // Override the default behavior, allowing the caller to specify response code, headers, and body + for (header in ref) { + value = ref[header]; + response.header(header, value); + } + body = result.body; + statusCode = result.statusCode; + } + response.send(statusCode, body); + return next(); + } + + }; + + Responder.prototype.codes = codes; + + return Responder; + +}).call(this); diff --git a/lib/response.js b/lib/response.js new file mode 100644 index 0000000..4f6509c --- /dev/null +++ b/lib/response.js @@ -0,0 +1,24 @@ +// Generated by CoffeeScript 2.5.1 +var RedirectResponse, Response, responder; + +responder = require('./responder'); + +exports.Response = Response = class Response { + constructor(body = null, headers = {}, statusCode1 = 200) { + this.body = body; + this.headers = headers; + this.statusCode = statusCode1; + } + +}; + +exports.RedirectResponse = RedirectResponse = class RedirectResponse extends Response { + constructor(uri, statusCode = 301) { + super(null, { + Location: uri + }, statusCode); + this.uri = uri; + this.statusCode = statusCode; + } + +}; diff --git a/lib/router.js b/lib/router.js new file mode 100644 index 0000000..b4203fb --- /dev/null +++ b/lib/router.js @@ -0,0 +1,95 @@ +// Generated by CoffeeScript 2.5.1 +'use strict'; +var Promise, error, registerRoute, restifyMethod, restifyPath, simplifySwaggerParams, swagger2; + +Promise = require('bluebird'); + +swagger2 = require('swagger2-utils'); + +error = require('./error'); + +restifyMethod = function(method) { + if (method === 'delete') { + return 'del'; + } else { + return method; + } +}; + +restifyPath = function(path) { + path = path.split('{').join(':'); + return path.split('}').join(''); +}; + +exports.simplifySwaggerParams = simplifySwaggerParams = function(swaggerParams = {}) { + var param, paramName, params, ref; + params = {}; + for (paramName in swaggerParams) { + param = swaggerParams[paramName]; + // The swagger library leaves numbers and booleans as strings when they're in the querystring or path, so fix them + switch ((ref = param.schema) != null ? ref.type : void 0) { + case 'number': + case 'integer': + if (typeof param.value !== 'number') { + param.value = param.value - 0; + } + break; + case 'boolean': + // swagger accepts the strings "true" and "false" as boolean values but doesn't convert them to boolean type. + if (typeof param.value !== 'boolean') { + param.value = (function() { + switch (param.value) { + case 'true': + return true; + case 'false': + return false; + default: + throw new Error(`Invalid boolean value: ${param.value}`); + } + })(); + } + } + params[paramName] = param.value; + } + return params; +}; + +registerRoute = function(server, method, path, handler) { + method = restifyMethod(method); + path = restifyPath(path); + return server[method]({ + url: path + }, function(request, response, next) { + return Promise.try(function() { + var params, ref; + // Create a simplified and type-corrected params object based on the swagger param data + params = simplifySwaggerParams(request != null ? (ref = request.swagger) != null ? ref.params : void 0 : void 0); + + // Hand off the request and params to the route's handler + return handler(request, params); + }).then(function(result) { + // Send the result to the responder + return server.responder.respond(result, response, next); + }).catch(function(err) { + return server.responder.errorResponse(err, response, next); + }); + }); +}; + +exports.registerRoutes = function(server, apiSpec, opts) { + var i, len, operation, operations, results, routeHandlers; + if (!opts.routeHandlers) { + throw new error.MissingRouteHandlersConfigError(); + } + routeHandlers = opts.routeHandlers; + operations = swagger2.createOperationsList(apiSpec); + results = []; + for (i = 0, len = operations.length; i < len; i++) { + operation = operations[i]; + if (routeHandlers[operation.operationId] == null) { + throw new error.MissingRouteHandlerError(operation); + } + results.push(registerRoute(server, operation.method, operation.path, routeHandlers[operation.operationId])); + } + return results; +}; diff --git a/lib/server.js b/lib/server.js new file mode 100644 index 0000000..ff9ca53 --- /dev/null +++ b/lib/server.js @@ -0,0 +1,129 @@ +// Generated by CoffeeScript 2.5.1 +'use strict'; +var Logger, Promise, Responder, addMiddleware, addParsers, configureLogging, error, initializeSwagger, response, restify, restifyPlugins, router, swaggerParameterReplacer, swaggerTools; + +Promise = require('bluebird'); + +restify = require('restify'); + +restifyPlugins = require('restify-plugins'); + +swaggerTools = require('swagger-tools'); + +Logger = require('./logger'); + +Responder = require('./responder'); + +router = require('./router'); + +error = require('./error'); + +swaggerParameterReplacer = require('./middleware/swaggerParameterReplacer'); + +response = require('./response'); + +exports.Response = response.Response; + +exports.RedirectResponse = response.RedirectResponse; + +addParsers = function(server, parsers) { + var i, len, parser, results; + if (Array.isArray(parsers)) { +// User specified parsers to use + results = []; + for (i = 0, len = parsers.length; i < len; i++) { + parser = parsers[i]; + results.push(server.use(parser.parser(parser.options))); + } + return results; + } else { + // Use the default parsers + server.use(restifyPlugins.bodyParser({ + mapParams: false + })); + server.use(restifyPlugins.authorizationParser()); + return server.use(restifyPlugins.queryParser()); + } +}; + +addMiddleware = function(server, middleware) { + var i, len, middlewareFunc, results; + if (Array.isArray(middleware)) { + results = []; + for (i = 0, len = middleware.length; i < len; i++) { + middlewareFunc = middleware[i]; + if (typeof middlewareFunc === 'function') { + results.push(server.use(middlewareFunc)); + } else { + results.push(void 0); + } + } + return results; + } +}; + +configureLogging = function(server, opts) { + var logger; + logger = new Logger(opts); + // Set up logging + server.on('after', restifyPlugins.auditLogger({ + name: 'audit', + log: logger.log + })); + Promise.onPossiblyUnhandledRejection(logger.unhandledRejection()); + process.on('uncaughtException', logger.unhandledProcessException()); + server.on('uncaughtException', logger.unhandledRestifyException()); + return server.logger = logger; +}; + +initializeSwagger = function(server, opts) { + if (!opts.api) { + throw new error.MissingApiConfigError(); + } + return new Promise(function(resolve) { + return swaggerTools.initializeMiddleware(opts.api, function(swaggerMiddleware) { + // Interpret Swagger resources and attach metadata to request.swagger + server.use(swaggerMiddleware.swaggerMetadata()); + + // Validate requests against the swagger metadata + server.use(swaggerMiddleware.swaggerValidator()); + + // Replace the swagger parameter values with those provided by restify since swagger doesn't URLdecode + server.use(swaggerParameterReplacer()); + return resolve(); + }); + }); +}; + +exports.createServer = function(opts) { + var server; + server = void 0; + return Promise.try(function() { + opts = opts || {}; + opts.restify = opts.restify || {}; + opts.middleware = opts.middleware || {}; + server = restify.createServer(opts.restify); + addParsers(server, opts.parsers); + addMiddleware(server, opts.middleware.afterParsers); + configureLogging(server, opts); + server.responder = new Responder(server.logger, opts.responder); + return initializeSwagger(server, opts); + }).then(function() { + var i, len, results, staticRoute, staticRoutes; + addMiddleware(server, opts.middleware.beforeRoutes); + // Add routes + router.registerRoutes(server, opts.api, opts); + // Add static serving if specified + staticRoutes = opts.serveStatic || []; + if (!staticRoutes.length) { // assume if we have an object if there is no length operator + staticRoutes = [staticRoutes]; + } +// now add any defined static routes to restify + results = []; + for (i = 0, len = staticRoutes.length; i < len; i++) { + staticRoute = staticRoutes[i]; + results.push(server.get(staticRoute.url, restifyPlugins.serveStatic(staticRoute))); + } + return results; + }).return(server); +};