PromiseRouter.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _node = _interopRequireDefault(require("parse/node"));
  7. var _express = _interopRequireDefault(require("express"));
  8. var _logger = _interopRequireDefault(require("./logger"));
  9. var _util = require("util");
  10. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  11. // A router that is based on promises rather than req/res/next.
  12. // This is intended to replace the use of express.Router to handle
  13. // subsections of the API surface.
  14. // This will make it easier to have methods like 'batch' that
  15. // themselves use our routing information, without disturbing express
  16. // components that external developers may be modifying.
  17. const Layer = require('express/lib/router/layer');
  18. function validateParameter(key, value) {
  19. if (key == 'className') {
  20. if (value.match(/_?[A-Za-z][A-Za-z_0-9]*/)) {
  21. return value;
  22. }
  23. } else if (key == 'objectId') {
  24. if (value.match(/[A-Za-z0-9]+/)) {
  25. return value;
  26. }
  27. } else {
  28. return value;
  29. }
  30. }
  31. class PromiseRouter {
  32. // Each entry should be an object with:
  33. // path: the path to route, in express format
  34. // method: the HTTP method that this route handles.
  35. // Must be one of: POST, GET, PUT, DELETE
  36. // handler: a function that takes request, and returns a promise.
  37. // Successful handlers should resolve to an object with fields:
  38. // status: optional. the http status code. defaults to 200
  39. // response: a json object with the content of the response
  40. // location: optional. a location header
  41. constructor(routes = [], appId) {
  42. this.routes = routes;
  43. this.appId = appId;
  44. this.mountRoutes();
  45. }
  46. // Leave the opportunity to
  47. // subclasses to mount their routes by overriding
  48. mountRoutes() {}
  49. // Merge the routes into this one
  50. merge(router) {
  51. for (var route of router.routes) {
  52. this.routes.push(route);
  53. }
  54. }
  55. route(method, path, ...handlers) {
  56. switch (method) {
  57. case 'POST':
  58. case 'GET':
  59. case 'PUT':
  60. case 'DELETE':
  61. break;
  62. default:
  63. throw 'cannot route method: ' + method;
  64. }
  65. let handler = handlers[0];
  66. if (handlers.length > 1) {
  67. handler = function (req) {
  68. return handlers.reduce((promise, handler) => {
  69. return promise.then(() => {
  70. return handler(req);
  71. });
  72. }, Promise.resolve());
  73. };
  74. }
  75. this.routes.push({
  76. path: path,
  77. method: method,
  78. handler: handler,
  79. layer: new Layer(path, null, handler)
  80. });
  81. }
  82. // Returns an object with:
  83. // handler: the handler that should deal with this request
  84. // params: any :-params that got parsed from the path
  85. // Returns undefined if there is no match.
  86. match(method, path) {
  87. for (var route of this.routes) {
  88. if (route.method != method) {
  89. continue;
  90. }
  91. const layer = route.layer || new Layer(route.path, null, route.handler);
  92. const match = layer.match(path);
  93. if (match) {
  94. const params = layer.params;
  95. Object.keys(params).forEach(key => {
  96. params[key] = validateParameter(key, params[key]);
  97. });
  98. return {
  99. params: params,
  100. handler: route.handler
  101. };
  102. }
  103. }
  104. }
  105. // Mount the routes on this router onto an express app (or express router)
  106. mountOnto(expressApp) {
  107. this.routes.forEach(route => {
  108. const method = route.method.toLowerCase();
  109. const handler = makeExpressHandler(this.appId, route.handler);
  110. expressApp[method].call(expressApp, route.path, handler);
  111. });
  112. return expressApp;
  113. }
  114. expressRouter() {
  115. return this.mountOnto(_express.default.Router());
  116. }
  117. tryRouteRequest(method, path, request) {
  118. var match = this.match(method, path);
  119. if (!match) {
  120. throw new _node.default.Error(_node.default.Error.INVALID_JSON, 'cannot route ' + method + ' ' + path);
  121. }
  122. request.params = match.params;
  123. return new Promise((resolve, reject) => {
  124. match.handler(request).then(resolve, reject);
  125. });
  126. }
  127. }
  128. // A helper function to make an express handler out of a a promise
  129. // handler.
  130. // Express handlers should never throw; if a promise handler throws we
  131. // just treat it like it resolved to an error.
  132. exports.default = PromiseRouter;
  133. function makeExpressHandler(appId, promiseHandler) {
  134. return function (req, res, next) {
  135. try {
  136. const url = maskSensitiveUrl(req);
  137. const body = Object.assign({}, req.body);
  138. const method = req.method;
  139. const headers = req.headers;
  140. _logger.default.logRequest({
  141. method,
  142. url,
  143. headers,
  144. body
  145. });
  146. promiseHandler(req).then(result => {
  147. if (!result.response && !result.location && !result.text) {
  148. _logger.default.error('the handler did not include a "response" or a "location" field');
  149. throw 'control should not get here';
  150. }
  151. _logger.default.logResponse({
  152. method,
  153. url,
  154. result
  155. });
  156. var status = result.status || 200;
  157. res.status(status);
  158. if (result.headers) {
  159. Object.keys(result.headers).forEach(header => {
  160. res.set(header, result.headers[header]);
  161. });
  162. }
  163. if (result.text) {
  164. res.send(result.text);
  165. return;
  166. }
  167. if (result.location) {
  168. res.set('Location', result.location);
  169. // Override the default expressjs response
  170. // as it double encodes %encoded chars in URL
  171. if (!result.response) {
  172. res.send('Found. Redirecting to ' + result.location);
  173. return;
  174. }
  175. }
  176. res.json(result.response);
  177. }, error => {
  178. next(error);
  179. }).catch(e => {
  180. _logger.default.error(`Error generating response. ${(0, _util.inspect)(e)}`, {
  181. error: e
  182. });
  183. next(e);
  184. });
  185. } catch (e) {
  186. _logger.default.error(`Error handling request: ${(0, _util.inspect)(e)}`, {
  187. error: e
  188. });
  189. next(e);
  190. }
  191. };
  192. }
  193. function maskSensitiveUrl(req) {
  194. let maskUrl = req.originalUrl.toString();
  195. const shouldMaskUrl = req.method === 'GET' && req.originalUrl.includes('/login') && !req.originalUrl.includes('classes');
  196. if (shouldMaskUrl) {
  197. maskUrl = _logger.default.maskSensitiveUrl(maskUrl);
  198. }
  199. return maskUrl;
  200. }
  201. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbm9kZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2V4cHJlc3MiLCJfbG9nZ2VyIiwiX3V0aWwiLCJlIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJMYXllciIsInZhbGlkYXRlUGFyYW1ldGVyIiwia2V5IiwidmFsdWUiLCJtYXRjaCIsIlByb21pc2VSb3V0ZXIiLCJjb25zdHJ1Y3RvciIsInJvdXRlcyIsImFwcElkIiwibW91bnRSb3V0ZXMiLCJtZXJnZSIsInJvdXRlciIsInJvdXRlIiwicHVzaCIsIm1ldGhvZCIsInBhdGgiLCJoYW5kbGVycyIsImhhbmRsZXIiLCJsZW5ndGgiLCJyZXEiLCJyZWR1Y2UiLCJwcm9taXNlIiwidGhlbiIsIlByb21pc2UiLCJyZXNvbHZlIiwibGF5ZXIiLCJwYXJhbXMiLCJPYmplY3QiLCJrZXlzIiwiZm9yRWFjaCIsIm1vdW50T250byIsImV4cHJlc3NBcHAiLCJ0b0xvd2VyQ2FzZSIsIm1ha2VFeHByZXNzSGFuZGxlciIsImNhbGwiLCJleHByZXNzUm91dGVyIiwiZXhwcmVzcyIsIlJvdXRlciIsInRyeVJvdXRlUmVxdWVzdCIsInJlcXVlc3QiLCJQYXJzZSIsIkVycm9yIiwiSU5WQUxJRF9KU09OIiwicmVqZWN0IiwiZXhwb3J0cyIsInByb21pc2VIYW5kbGVyIiwicmVzIiwibmV4dCIsInVybCIsIm1hc2tTZW5zaXRpdmVVcmwiLCJib2R5IiwiYXNzaWduIiwiaGVhZGVycyIsImxvZyIsImxvZ1JlcXVlc3QiLCJyZXN1bHQiLCJyZXNwb25zZSIsImxvY2F0aW9uIiwidGV4dCIsImVycm9yIiwibG9nUmVzcG9uc2UiLCJzdGF0dXMiLCJoZWFkZXIiLCJzZXQiLCJzZW5kIiwianNvbiIsImNhdGNoIiwiaW5zcGVjdCIsIm1hc2tVcmwiLCJvcmlnaW5hbFVybCIsInRvU3RyaW5nIiwic2hvdWxkTWFza1VybCIsImluY2x1ZGVzIl0sInNvdXJjZXMiOlsiLi4vc3JjL1Byb21pc2VSb3V0ZXIuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gQSByb3V0ZXIgdGhhdCBpcyBiYXNlZCBvbiBwcm9taXNlcyByYXRoZXIgdGhhbiByZXEvcmVzL25leHQuXG4vLyBUaGlzIGlzIGludGVuZGVkIHRvIHJlcGxhY2UgdGhlIHVzZSBvZiBleHByZXNzLlJvdXRlciB0byBoYW5kbGVcbi8vIHN1YnNlY3Rpb25zIG9mIHRoZSBBUEkgc3VyZmFjZS5cbi8vIFRoaXMgd2lsbCBtYWtlIGl0IGVhc2llciB0byBoYXZlIG1ldGhvZHMgbGlrZSAnYmF0Y2gnIHRoYXRcbi8vIHRoZW1zZWx2ZXMgdXNlIG91ciByb3V0aW5nIGluZm9ybWF0aW9uLCB3aXRob3V0IGRpc3R1cmJpbmcgZXhwcmVzc1xuLy8gY29tcG9uZW50cyB0aGF0IGV4dGVybmFsIGRldmVsb3BlcnMgbWF5IGJlIG1vZGlmeWluZy5cblxuaW1wb3J0IFBhcnNlIGZyb20gJ3BhcnNlL25vZGUnO1xuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgbG9nIGZyb20gJy4vbG9nZ2VyJztcbmltcG9ydCB7IGluc3BlY3QgfSBmcm9tICd1dGlsJztcbmNvbnN0IExheWVyID0gcmVxdWlyZSgnZXhwcmVzcy9saWIvcm91dGVyL2xheWVyJyk7XG5cbmZ1bmN0aW9uIHZhbGlkYXRlUGFyYW1ldGVyKGtleSwgdmFsdWUpIHtcbiAgaWYgKGtleSA9PSAnY2xhc3NOYW1lJykge1xuICAgIGlmICh2YWx1ZS5tYXRjaCgvXz9bQS1aYS16XVtBLVphLXpfMC05XSovKSkge1xuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cbiAgfSBlbHNlIGlmIChrZXkgPT0gJ29iamVjdElkJykge1xuICAgIGlmICh2YWx1ZS5tYXRjaCgvW0EtWmEtejAtOV0rLykpIHtcbiAgICAgIHJldHVybiB2YWx1ZTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIHZhbHVlO1xuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFByb21pc2VSb3V0ZXIge1xuICAvLyBFYWNoIGVudHJ5IHNob3VsZCBiZSBhbiBvYmplY3Qgd2l0aDpcbiAgLy8gcGF0aDogdGhlIHBhdGggdG8gcm91dGUsIGluIGV4cHJlc3MgZm9ybWF0XG4gIC8vIG1ldGhvZDogdGhlIEhUVFAgbWV0aG9kIHRoYXQgdGhpcyByb3V0ZSBoYW5kbGVzLlxuICAvLyAgIE11c3QgYmUgb25lIG9mOiBQT1NULCBHRVQsIFBVVCwgREVMRVRFXG4gIC8vIGhhbmRsZXI6IGEgZnVuY3Rpb24gdGhhdCB0YWtlcyByZXF1ZXN0LCBhbmQgcmV0dXJucyBhIHByb21pc2UuXG4gIC8vICAgU3VjY2Vzc2Z1bCBoYW5kbGVycyBzaG91bGQgcmVzb2x2ZSB0byBhbiBvYmplY3Qgd2l0aCBmaWVsZHM6XG4gIC8vICAgICBzdGF0dXM6IG9wdGlvbmFsLiB0aGUgaHR0cCBzdGF0dXMgY29kZS4gZGVmYXVsdHMgdG8gMjAwXG4gIC8vICAgICByZXNwb25zZTogYSBqc29uIG9iamVjdCB3aXRoIHRoZSBjb250ZW50IG9mIHRoZSByZXNwb25zZVxuICAvLyAgICAgbG9jYXRpb246IG9wdGlvbmFsLiBhIGxvY2F0aW9uIGhlYWRlclxuICBjb25zdHJ1Y3Rvcihyb3V0ZXMgPSBbXSwgYXBwSWQpIHtcbiAgICB0aGlzLnJvdXRlcyA9IHJvdXRlcztcbiAgICB0aGlzLmFwcElkID0gYXBwSWQ7XG4gICAgdGhpcy5tb3VudFJvdXRlcygpO1xuICB9XG5cbiAgLy8gTGVhdmUgdGhlIG9wcG9ydHVuaXR5IHRvXG4gIC8vIHN1YmNsYXNzZXMgdG8gbW91bnQgdGhlaXIgcm91dGVzIGJ5IG92ZXJyaWRpbmdcbiAgbW91bnRSb3V0ZXMoKSB7fVxuXG4gIC8vIE1lcmdlIHRoZSByb3V0ZXMgaW50byB0aGlzIG9uZVxuICBtZXJnZShyb3V0ZXIpIHtcbiAgICBmb3IgKHZhciByb3V0ZSBvZiByb3V0ZXIucm91dGVzKSB7XG4gICAgICB0aGlzLnJvdXRlcy5wdXNoKHJvdXRlKTtcbiAgICB9XG4gIH1cblxuICByb3V0ZShtZXRob2QsIHBhdGgsIC4uLmhhbmRsZXJzKSB7XG4gICAgc3dpdGNoIChtZXRob2QpIHtcbiAgICAgIGNhc2UgJ1BPU1QnOlxuICAgICAgY2FzZSAnR0VUJzpcbiAgICAgIGNhc2UgJ1BVVCc6XG4gICAgICBjYXNlICdERUxFVEUnOlxuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHRocm93ICdjYW5ub3Qgcm91dGUgbWV0aG9kOiAnICsgbWV0aG9kO1xuICAgIH1cblxuICAgIGxldCBoYW5kbGVyID0gaGFuZGxlcnNbMF07XG5cbiAgICBpZiAoaGFuZGxlcnMubGVuZ3RoID4gMSkge1xuICAgICAgaGFuZGxlciA9IGZ1bmN0aW9uIChyZXEpIHtcbiAgICAgICAgcmV0dXJuIGhhbmRsZXJzLnJlZHVjZSgocHJvbWlzZSwgaGFuZGxlcikgPT4ge1xuICAgICAgICAgIHJldHVybiBwcm9taXNlLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIGhhbmRsZXIocmVxKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSwgUHJvbWlzZS5yZXNvbHZlKCkpO1xuICAgICAgfTtcbiAgICB9XG5cbiAgICB0aGlzLnJvdXRlcy5wdXNoKHtcbiAgICAgIHBhdGg6IHBhdGgsXG4gICAgICBtZXRob2Q6IG1ldGhvZCxcbiAgICAgIGhhbmRsZXI6IGhhbmRsZXIsXG4gICAgICBsYXllcjogbmV3IExheWVyKHBhdGgsIG51bGwsIGhhbmRsZXIpLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gUmV0dXJucyBhbiBvYmplY3Qgd2l0aDpcbiAgLy8gICBoYW5kbGVyOiB0aGUgaGFuZGxlciB0aGF0IHNob3VsZCBkZWFsIHdpdGggdGhpcyByZXF1ZXN0XG4gIC8vICAgcGFyYW1zOiBhbnkgOi1wYXJhbXMgdGhhdCBnb3QgcGFyc2VkIGZyb20gdGhlIHBhdGhcbiAgLy8gUmV0dXJucyB1bmRlZmluZWQgaWYgdGhlcmUgaXMgbm8gbWF0Y2guXG4gIG1hdGNoKG1ldGhvZCwgcGF0aCkge1xuICAgIGZvciAodmFyIHJvdXRlIG9mIHRoaXMucm91dGVzKSB7XG4gICAgICBpZiAocm91dGUubWV0aG9kICE9IG1ldGhvZCkge1xuICAgICAgICBjb250aW51ZTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGxheWVyID0gcm91dGUubGF5ZXIgfHwgbmV3IExheWVyKHJvdXRlLnBhdGgsIG51bGwsIHJvdXRlLmhhbmRsZXIpO1xuICAgICAgY29uc3QgbWF0Y2ggPSBsYXllci5tYXRjaChwYXRoKTtcbiAgICAgIGlmIChtYXRjaCkge1xuICAgICAgICBjb25zdCBwYXJhbXMgPSBsYXllci5wYXJhbXM7XG4gICAgICAgIE9iamVjdC5rZXlzKHBhcmFtcykuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgICAgIHBhcmFtc1trZXldID0gdmFsaWRhdGVQYXJhbWV0ZXIoa2V5LCBwYXJhbXNba2V5XSk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4geyBwYXJhbXM6IHBhcmFtcywgaGFuZGxlcjogcm91dGUuaGFuZGxlciB9O1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIE1vdW50IHRoZSByb3V0ZXMgb24gdGhpcyByb3V0ZXIgb250byBhbiBleHByZXNzIGFwcCAob3IgZXhwcmVzcyByb3V0ZXIpXG4gIG1vdW50T250byhleHByZXNzQXBwKSB7XG4gICAgdGhpcy5yb3V0ZXMuZm9yRWFjaChyb3V0ZSA9PiB7XG4gICAgICBjb25zdCBtZXRob2QgPSByb3V0ZS5tZXRob2QudG9Mb3dlckNhc2UoKTtcbiAgICAgIGNvbnN0IGhhbmRsZXIgPSBtYWtlRXhwcmVzc0hhbmRsZXIodGhpcy5hcHBJZCwgcm91dGUuaGFuZGxlcik7XG4gICAgICBleHByZXNzQXBwW21ldGhvZF0uY2FsbChleHByZXNzQXBwLCByb3V0ZS5wYXRoLCBoYW5kbGVyKTtcbiAgICB9KTtcbiAgICByZXR1cm4gZXhwcmVzc0FwcDtcbiAgfVxuXG4gIGV4cHJlc3NSb3V0ZXIoKSB7XG4gICAgcmV0dXJuIHRoaXMubW91bnRPbnRvKGV4cHJlc3MuUm91dGVyKCkpO1xuICB9XG5cbiAgdHJ5Um91dGVSZXF1ZXN0KG1ldGhvZCwgcGF0aCwgcmVxdWVzdCkge1xuICAgIHZhciBtYXRjaCA9IHRoaXMubWF0Y2gobWV0aG9kLCBwYXRoKTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoUGFyc2UuRXJyb3IuSU5WQUxJRF9KU09OLCAnY2Fubm90IHJvdXRlICcgKyBtZXRob2QgKyAnICcgKyBwYXRoKTtcbiAgICB9XG4gICAgcmVxdWVzdC5wYXJhbXMgPSBtYXRjaC5wYXJhbXM7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIG1hdGNoLmhhbmRsZXIocmVxdWVzdCkudGhlbihyZXNvbHZlLCByZWplY3QpO1xuICAgIH0pO1xuICB9XG59XG5cbi8vIEEgaGVscGVyIGZ1bmN0aW9uIHRvIG1ha2UgYW4gZXhwcmVzcyBoYW5kbGVyIG91dCBvZiBhIGEgcHJvbWlzZVxuLy8gaGFuZGxlci5cbi8vIEV4cHJlc3MgaGFuZGxlcnMgc2hvdWxkIG5ldmVyIHRocm93OyBpZiBhIHByb21pc2UgaGFuZGxlciB0aHJvd3Mgd2Vcbi8vIGp1c3QgdHJlYXQgaXQgbGlrZSBpdCByZXNvbHZlZCB0byBhbiBlcnJvci5cbmZ1bmN0aW9uIG1ha2VFeHByZXNzSGFuZGxlcihhcHBJZCwgcHJvbWlzZUhhbmRsZXIpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIChyZXEsIHJlcywgbmV4dCkge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCB1cmwgPSBtYXNrU2Vuc2l0aXZlVXJsKHJlcSk7XG4gICAgICBjb25zdCBib2R5ID0gT2JqZWN0LmFzc2lnbih7fSwgcmVxLmJvZHkpO1xuICAgICAgY29uc3QgbWV0aG9kID0gcmVxLm1ldGhvZDtcbiAgICAgIGNvbnN0IGhlYWRlcnMgPSByZXEuaGVhZGVycztcbiAgICAgIGxvZy5sb2dSZXF1ZXN0KHtcbiAgICAgICAgbWV0aG9kLFxuICAgICAgICB1cmwsXG4gICAgICAgIGhlYWRlcnMsXG4gICAgICAgIGJvZHksXG4gICAgICB9KTtcbiAgICAgIHByb21pc2VIYW5kbGVyKHJlcSlcbiAgICAgICAgLnRoZW4oXG4gICAgICAgICAgcmVzdWx0ID0+IHtcbiAgICAgICAgICAgIGlmICghcmVzdWx0LnJlc3BvbnNlICYmICFyZXN1bHQubG9jYXRpb24gJiYgIXJlc3VsdC50ZXh0KSB7XG4gICAgICAgICAgICAgIGxvZy5lcnJvcigndGhlIGhhbmRsZXIgZGlkIG5vdCBpbmNsdWRlIGEgXCJyZXNwb25zZVwiIG9yIGEgXCJsb2NhdGlvblwiIGZpZWxkJyk7XG4gICAgICAgICAgICAgIHRocm93ICdjb250cm9sIHNob3VsZCBub3QgZ2V0IGhlcmUnO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBsb2cubG9nUmVzcG9uc2UoeyBtZXRob2QsIHVybCwgcmVzdWx0IH0pO1xuXG4gICAgICAgICAgICB2YXIgc3RhdHVzID0gcmVzdWx0LnN0YXR1cyB8fCAyMDA7XG4gICAgICAgICAgICByZXMuc3RhdHVzKHN0YXR1cyk7XG5cbiAgICAgICAgICAgIGlmIChyZXN1bHQuaGVhZGVycykge1xuICAgICAgICAgICAgICBPYmplY3Qua2V5cyhyZXN1bHQuaGVhZGVycykuZm9yRWFjaChoZWFkZXIgPT4ge1xuICAgICAgICAgICAgICAgIHJlcy5zZXQoaGVhZGVyLCByZXN1bHQuaGVhZGVyc1toZWFkZXJdKTtcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChyZXN1bHQudGV4dCkge1xuICAgICAgICAgICAgICByZXMuc2VuZChyZXN1bHQudGV4dCk7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHJlc3VsdC5sb2NhdGlvbikge1xuICAgICAgICAgICAgICByZXMuc2V0KCdMb2NhdGlvbicsIHJlc3VsdC5sb2NhdGlvbik7XG4gICAgICAgICAgICAgIC8vIE92ZXJyaWRlIHRoZSBkZWZhdWx0IGV4cHJlc3NqcyByZXNwb25zZVxuICAgICAgICAgICAgICAvLyBhcyBpdCBkb3VibGUgZW5jb2RlcyAlZW5jb2RlZCBjaGFycyBpbiBVUkxcbiAgICAgICAgICAgICAgaWYgKCFyZXN1bHQucmVzcG9uc2UpIHtcbiAgICAgICAgICAgICAgICByZXMuc2VuZCgnRm91bmQuIFJlZGlyZWN0aW5nIHRvICcgKyByZXN1bHQubG9jYXRpb24pO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmVzLmpzb24ocmVzdWx0LnJlc3BvbnNlKTtcbiAgICAgICAgICB9LFxuICAgICAgICAgIGVycm9yID0+IHtcbiAgICAgICAgICAgIG5leHQoZXJyb3IpO1xuICAgICAgICAgIH1cbiAgICAgICAgKVxuICAgICAgICAuY2F0Y2goZSA9PiB7XG4gICAgICAgICAgbG9nLmVycm9yKGBFcnJvciBnZW5lcmF0aW5nIHJlc3BvbnNlLiAke2luc3BlY3QoZSl9YCwgeyBlcnJvcjogZSB9KTtcbiAgICAgICAgICBuZXh0KGUpO1xuICAgICAgICB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2cuZXJyb3IoYEVycm9yIGhhbmRsaW5nIHJlcXVlc3Q6ICR7aW5zcGVjdChlKX1gLCB7IGVycm9yOiBlIH0pO1xuICAgICAgbmV4dChlKTtcbiAgICB9XG4gIH07XG59XG5cbmZ1bmN0aW9uIG1hc2tTZW5zaXRpdmVVcmwocmVxKSB7XG4gIGxldCBtYXNrVXJsID0gcmVxLm9yaWdpbmFsVXJsLnRvU3RyaW5nKCk7XG4gIGNvbnN0IHNob3VsZE1hc2tVcmwgPVxuICAgIHJlcS5tZXRob2QgPT09ICdHRVQnICYmXG4gICAgcmVxLm9yaWdpbmFsVXJsLmluY2x1ZGVzKCcvbG9naW4nKSAmJlxuICAgICFyZXEub3JpZ2luYWxVcmwuaW5jbHVkZXMoJ2NsYXNzZXMnKTtcbiAgaWYgKHNob3VsZE1hc2tVcmwpIHtcbiAgICBtYXNrVXJsID0gbG9nLm1hc2tTZW5zaXRpdmVVcmwobWFza1VybCk7XG4gIH1cbiAgcmV0dXJuIG1hc2tVcmw7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQU9BLElBQUFBLEtBQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLFFBQUEsR0FBQUYsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFFLE9BQUEsR0FBQUgsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFHLEtBQUEsR0FBQUgsT0FBQTtBQUErQixTQUFBRCx1QkFBQUssQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFBLEtBQUFFLE9BQUEsRUFBQUYsQ0FBQTtBQVYvQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBTUEsTUFBTUcsS0FBSyxHQUFHUCxPQUFPLENBQUMsMEJBQTBCLENBQUM7QUFFakQsU0FBU1EsaUJBQWlCQSxDQUFDQyxHQUFHLEVBQUVDLEtBQUssRUFBRTtFQUNyQyxJQUFJRCxHQUFHLElBQUksV0FBVyxFQUFFO0lBQ3RCLElBQUlDLEtBQUssQ0FBQ0MsS0FBSyxDQUFDLHlCQUF5QixDQUFDLEVBQUU7TUFDMUMsT0FBT0QsS0FBSztJQUNkO0VBQ0YsQ0FBQyxNQUFNLElBQUlELEdBQUcsSUFBSSxVQUFVLEVBQUU7SUFDNUIsSUFBSUMsS0FBSyxDQUFDQyxLQUFLLENBQUMsY0FBYyxDQUFDLEVBQUU7TUFDL0IsT0FBT0QsS0FBSztJQUNkO0VBQ0YsQ0FBQyxNQUFNO0lBQ0wsT0FBT0EsS0FBSztFQUNkO0FBQ0Y7QUFFZSxNQUFNRSxhQUFhLENBQUM7RUFDakM7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0FDLFdBQVdBLENBQUNDLE1BQU0sR0FBRyxFQUFFLEVBQUVDLEtBQUssRUFBRTtJQUM5QixJQUFJLENBQUNELE1BQU0sR0FBR0EsTUFBTTtJQUNwQixJQUFJLENBQUNDLEtBQUssR0FBR0EsS0FBSztJQUNsQixJQUFJLENBQUNDLFdBQVcsQ0FB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QjtRQUNGO1FBRUEsSUFBSUgsTUFBTSxDQUFDRSxRQUFRLEVBQUU7VUFDbkJYLEdBQUcsQ0FBQ2lCLEdBQUcsQ0FBQyxVQUFVLEVBQUVSLE1BQU0sQ0FBQ0UsUUFBUSxDQUFDO1VBQ3BDO1VBQ0E7VUFDQSxJQUFJLENBQUNGLE1BQU0sQ0FBQ0MsUUFBUSxFQUFFO1lBQ3BCVixHQUFHLENBQUNrQixJQUFJLENBQUMsd0JBQXdCLEdBQUdULE1BQU0sQ0FBQ0UsUUFBUSxDQUFDO1lBQ3BEO1VBQ0Y7UUFDRjtRQUNBWCxHQUFHLENBQUNtQixJQUFJLENBQUNWLE1BQU0sQ0FBQ0MsUUFBUSxDQUFDO01BQzNCLENBQUMsRUFDREcsS0FBSyxJQUFJO1FBQ1BaLElBQUksQ0FBQ1ksS0FBSyxDQUFDO01BQ2IsQ0FDRixDQUFDLENBQ0FPLEtBQUssQ0FBQ3JFLENBQUMsSUFBSTtRQUNWd0QsZUFBRyxDQUFDTSxLQUFLLENBQUMsOEJBQThCLElBQUFRLGFBQU8sRUFBQ3RFLENBQUMsQ0FBQyxFQUFFLEVBQUU7VUFBRThELEtBQUssRUFBRTlEO1FBQUUsQ0FBQyxDQUFDO1FBQ25Fa0QsSUFBSSxDQUFDbEQsQ0FBQyxDQUFDO01BQ1QsQ0FBQyxDQUFDO0lBQ04sQ0FBQyxDQUFDLE9BQU9BLENBQUMsRUFBRTtNQUNWd0QsZUFBRyxDQUFDTSxLQUFLLENBQUMsMkJBQTJCLElBQUFRLGFBQU8sRUFBQ3RFLENBQUMsQ0FBQyxFQUFFLEVBQUU7UUFBRThELEtBQUssRUFBRTlEO01BQUUsQ0FBQyxDQUFDO01BQ2hFa0QsSUFBSSxDQUFDbEQsQ0FBQyxDQUFDO0lBQ1Q7RUFDRixDQUFDO0FBQ0g7QUFFQSxTQUFTb0QsZ0JBQWdCQSxDQUFDOUIsR0FBRyxFQUFFO0VBQzdCLElBQUlpRCxPQUFPLEdBQUdqRCxHQUFHLENBQUNrRCxXQUFXLENBQUNDLFFBQVEsQ0FBQyxDQUFDO0VBQ3hDLE1BQU1DLGFBQWEsR0FDakJwRCxHQUFHLENBQUNMLE1BQU0sS0FBSyxLQUFLLElBQ3BCSyxHQUFHLENBQUNrRCxXQUFXLENBQUNHLFFBQVEsQ0FBQyxRQUFRLENBQUMsSUFDbEMsQ0FBQ3JELEdBQUcsQ0FBQ2tELFdBQVcsQ0FBQ0csUUFBUSxDQUFDLFNBQVMsQ0FBQztFQUN0QyxJQUFJRCxhQUFhLEVBQUU7SUFDakJILE9BQU8sR0FBR2YsZUFBRyxDQUFDSixnQkFBZ0IsQ0FBQ21CLE9BQU8sQ0FBQztFQUN6QztFQUNBLE9BQU9BLE9BQU87QUFDaEIiLCJpZ25vcmVMaXN0IjpbXX0=