From c2badcbcb3710109ec0b87847ceb9311dbc7705a Mon Sep 17 00:00:00 2001 From: Himani Raghav Date: Thu, 12 Jan 2023 15:32:03 +0530 Subject: [PATCH 1/4] axios-retry added --- config/default.js | 5 + package-lock.json | 149 +++-- package.json | 1 + src/common/helper.js | 1306 +++++++++++++++++++++++++----------------- 4 files changed, 873 insertions(+), 588 deletions(-) diff --git a/config/default.js b/config/default.js index babe7a5e..d33e027b 100644 --- a/config/default.js +++ b/config/default.js @@ -98,6 +98,11 @@ module.exports = { WORK_COMPLETED: process.env.WORK_COMPLETED || '' }, + AXIOS_RETRY: { + RETRIES: process.env.RETRIES || 3, + RETRY_COUNT: process.env.RETRY_COUNT || 1 + }, + EMAIL_FROM: process.env.EMAIL_FROM || 'no-reply@topcoder.com', SELF_SERVICE_EMAIL_CC_ACCOUNTS: process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS ? _.map(process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS.split(','), email => ({ email })) : [{ email: 'sathya.jayabal@gmail.com' }], SELF_SERVICE_WHITELIST_HANDLES: process.env.SELF_SERVICE_WHITELIST_HANDLES ? process.env.SELF_SERVICE_WHITELIST_HANDLES.split(',') : ['TCConnCopilot', 'sstestcopilot'], diff --git a/package-lock.json b/package-lock.json index a70c4fae..8c0bc8cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,6 +112,14 @@ "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", "dev": true }, + "@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@babel/template": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", @@ -548,6 +556,15 @@ "follow-redirects": "1.5.10" } }, + "axios-retry": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", + "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", + "requires": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -866,7 +883,7 @@ "semver": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.1.tgz", - "integrity": "sha1-n7P0AE+QDYPEeWj+QvdYPgWDLMk=" + "integrity": "sha512-Ne6/HdGZvvpXBdjW3o8J0pvxC2jnmVNBK7MKkMgsOBfrsIdTXfA5x+H9DUbQ2xzyvnLv0A0v9x8R4B40xNZIRQ==" } } }, @@ -1507,7 +1524,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -1747,7 +1764,7 @@ "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", "dev": true, "requires": { "pify": "^2.0.0" @@ -1756,13 +1773,13 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", "dev": true, "requires": { "load-json-file": "^2.0.0", @@ -1773,7 +1790,7 @@ "read-pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", "dev": true, "requires": { "find-up": "^2.0.0", @@ -2607,7 +2624,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -2746,6 +2763,11 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" + }, "is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -3170,7 +3192,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true } } @@ -3829,18 +3851,18 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "path-parse": { @@ -3852,7 +3874,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "path-type": { "version": "3.0.0", @@ -3866,7 +3888,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true } } @@ -3880,7 +3902,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "pify": { "version": "4.0.1", @@ -3891,7 +3913,7 @@ "pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, "requires": { "find-up": "^2.0.0", @@ -3946,7 +3968,7 @@ "pkg-config": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "integrity": "sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw==", "dev": true, "requires": { "debug-log": "^1.0.0", @@ -3972,12 +3994,12 @@ "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true }, "process-nextick-args": { @@ -4019,7 +4041,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" }, "psl": { "version": "1.8.0", @@ -4029,12 +4051,12 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" }, "qs": { "version": "6.10.3", @@ -4047,7 +4069,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, "queue-microtask": { "version": "1.2.3", @@ -4076,7 +4098,7 @@ "semver": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", - "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU=" + "integrity": "sha512-sfKXKhcz5XVyfUZa2V4RbjK0xjOJCMLNF9H4p4v0UCo9wNHM/lH9RDuyDbGEtxWLMDlPBc8xI7AbbVLKXty+rQ==" } } }, @@ -4105,7 +4127,7 @@ "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "requires": { "load-json-file": "^4.0.0", @@ -4147,11 +4169,16 @@ "reconnect-core": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", - "integrity": "sha1-+65SkZp4d9hE4yRtAaLyZwHIM8g=", + "integrity": "sha512-+gLKwmyRf2tjl6bLR03DoeWELzyN6LW9Xgr3vh7NXHHwPi0JC0N2TwPyf90oUEBkCRcD+bgQ+s3HORoG3nwHDg==", "requires": { "backoff": "~2.5.0" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -4172,7 +4199,7 @@ "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { "es6-error": "^4.0.1" @@ -4215,7 +4242,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { @@ -4227,7 +4254,7 @@ "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", "dev": true, "requires": { "caller-path": "^0.1.0", @@ -4237,7 +4264,7 @@ "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", "dev": true } } @@ -4262,7 +4289,7 @@ "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "requires": { "onetime": "^2.0.0", @@ -4272,7 +4299,7 @@ "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", "optional": true, "requires": { "glob": "^6.0.1" @@ -4326,7 +4353,7 @@ "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, "semver": { "version": "5.7.1", @@ -4389,7 +4416,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "setprototypeof": { @@ -4400,7 +4427,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -4409,7 +4436,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true }, "side-channel": { @@ -4431,7 +4458,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "^0.3.1" } @@ -4525,7 +4552,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "sshpk": { "version": "1.17.0", @@ -4546,7 +4573,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" }, "standard": { "version": "12.0.1", @@ -4654,7 +4681,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "requires": { "ansi-regex": "^2.0.0" } @@ -4662,13 +4689,13 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true }, "superagent": { @@ -4691,7 +4718,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" }, "supports-preserve-symlinks-flag": { "version": "1.0.0", @@ -4715,7 +4742,7 @@ "symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", "dev": true }, "table": { @@ -4813,13 +4840,13 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { @@ -4834,7 +4861,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "toidentifier": { @@ -4901,7 +4928,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "requires": { "safe-buffer": "^5.0.1" } @@ -4909,12 +4936,12 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -4950,13 +4977,13 @@ "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", "dev": true }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "uri-js": { "version": "4.4.1", @@ -4976,7 +5003,7 @@ "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -4985,12 +5012,12 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.4.0", @@ -5010,12 +5037,12 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -5047,7 +5074,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "wide-align": { @@ -5167,12 +5194,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", "dev": true, "requires": { "mkdirp": "^0.5.1" @@ -5201,7 +5228,7 @@ "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, "xss": { "version": "1.0.11", @@ -5227,7 +5254,7 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, "yamljs": { "version": "0.3.0", diff --git a/package.json b/package.json index 3d2b8733..533a2cfe 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "dependencies": { "aws-sdk": "^2.1145.0", "axios": "^0.19.0", + "axios-retry": "^3.3.1", "bluebird": "^3.5.1", "body-parser": "^1.15.1", "config": "^3.0.1", diff --git a/src/common/helper.js b/src/common/helper.js index 0db4a781..0e28f266 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -1,75 +1,78 @@ /** * This file defines helper methods */ -const Joi = require('joi') -const _ = require('lodash') -const querystring = require('querystring') -const constants = require('../../app-constants') -const models = require('../models') -const errors = require('./errors') -const util = require('util') -const AWS = require('aws-sdk') -const config = require('config') -const m2mAuth = require('tc-core-library-js').auth.m2m -const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME'])) -const axios = require('axios') -const busApi = require('topcoder-bus-api-wrapper') -const elasticsearch = require('elasticsearch') -const NodeCache = require('node-cache') -const HttpStatus = require('http-status-codes') -const xss = require('xss') -const logger = require('./logger') +const Joi = require("joi"); +const _ = require("lodash"); +const querystring = require("querystring"); +const constants = require("../../app-constants"); +const models = require("../models"); +const errors = require("./errors"); +const util = require("util"); +const AWS = require("aws-sdk"); +const config = require("config"); +const m2mAuth = require("tc-core-library-js").auth.m2m; +const m2m = m2mAuth( + _.pick(config, ["AUTH0_URL", "AUTH0_AUDIENCE", "TOKEN_CACHE_TIME"]) +); +const axios = require("axios"); +const axiosRetry = require("axios-retry"); +const busApi = require("topcoder-bus-api-wrapper"); +const elasticsearch = require("elasticsearch"); +const NodeCache = require("node-cache"); +const HttpStatus = require("http-status-codes"); +const xss = require("xss"); +const logger = require("./logger"); // Bus API Client -let busApiClient +let busApiClient; // Elasticsearch client -let esClient +let esClient; // validate ES refresh method -validateESRefreshMethod(config.ES.ES_REFRESH) +validateESRefreshMethod(config.ES.ES_REFRESH); AWS.config.update({ s3: config.AMAZON.S3_API_VERSION, // accessKeyId: config.AMAZON.AWS_ACCESS_KEY_ID, // secretAccessKey: config.AMAZON.AWS_SECRET_ACCESS_KEY, - region: config.AMAZON.AWS_REGION -}) -const s3 = new AWS.S3() + region: config.AMAZON.AWS_REGION, +}); +const s3 = new AWS.S3(); /** * Wrap async function to standard express function * @param {Function} fn the async function * @returns {Function} the wrapped function */ -function wrapExpress (fn) { +function wrapExpress(fn) { return function (req, res, next) { - fn(req, res, next).catch(next) - } + fn(req, res, next).catch(next); + }; } // Internal cache -const internalCache = new NodeCache({ stdTTL: config.INTERNAL_CACHE_TTL }) +const internalCache = new NodeCache({ stdTTL: config.INTERNAL_CACHE_TTL }); /** * Wrap all functions from object * @param obj the object (controller exports) * @returns {Object|Array} the wrapped object */ -function autoWrapExpress (obj) { +function autoWrapExpress(obj) { if (_.isArray(obj)) { - return obj.map(autoWrapExpress) + return obj.map(autoWrapExpress); } if (_.isFunction(obj)) { - if (obj.constructor.name === 'AsyncFunction') { - return wrapExpress(obj) + if (obj.constructor.name === "AsyncFunction") { + return wrapExpress(obj); } - return obj + return obj; } _.each(obj, (value, key) => { - obj[key] = autoWrapExpress(value) - }) - return obj + obj[key] = autoWrapExpress(value); + }); + return obj; } /** @@ -78,9 +81,9 @@ function autoWrapExpress (obj) { * @param {Number} page the page number * @returns {String} link for the page */ -function getPageLink (req, page) { - const q = _.assignIn({}, req.query, { page }) - return `${config.API_BASE_URL}${req.path}?${querystring.stringify(q)}` +function getPageLink(req, page) { + const q = _.assignIn({}, req.query, { page }); + return `${config.API_BASE_URL}${req.path}?${querystring.stringify(q)}`; } /** @@ -89,28 +92,37 @@ function getPageLink (req, page) { * @param {Object} res the HTTP response * @param {Object} result the operation result */ -function setResHeaders (req, res, result) { - const totalPages = Math.ceil(result.total / result.perPage) +function setResHeaders(req, res, result) { + const totalPages = Math.ceil(result.total / result.perPage); if (parseInt(result.page, 10) > 1) { - res.set('X-Prev-Page', parseInt(result.page, 10) - 1) + res.set("X-Prev-Page", parseInt(result.page, 10) - 1); } if (parseInt(result.page, 10) < totalPages) { - res.set('X-Next-Page', parseInt(result.page, 10) + 1) + res.set("X-Next-Page", parseInt(result.page, 10) + 1); } - res.set('X-Page', parseInt(result.page, 10)) - res.set('X-Per-Page', result.perPage) - res.set('X-Total', result.total) - res.set('X-Total-Pages', totalPages) + res.set("X-Page", parseInt(result.page, 10)); + res.set("X-Per-Page", result.perPage); + res.set("X-Total", result.total); + res.set("X-Total-Pages", totalPages); // set Link header if (totalPages > 0) { - let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink(req, totalPages)}>; rel="last"` + let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink( + req, + totalPages + )}>; rel="last"`; if (parseInt(result.page, 10) > 1) { - link += `, <${getPageLink(req, parseInt(result.page, 10) - 1)}>; rel="prev"` + link += `, <${getPageLink( + req, + parseInt(result.page, 10) - 1 + )}>; rel="prev"`; } if (parseInt(result.page, 10) < totalPages) { - link += `, <${getPageLink(req, parseInt(result.page, 10) + 1)}>; rel="next"` + link += `, <${getPageLink( + req, + parseInt(result.page, 10) + 1 + )}>; rel="next"`; } - res.set('Link', link) + res.set("Link", link); } } @@ -118,15 +130,18 @@ function setResHeaders (req, res, result) { * Check if the user has admin role * @param {Object} authUser the user */ -function hasAdminRole (authUser) { +function hasAdminRole(authUser) { if (authUser && authUser.roles) { for (let i = 0; i < authUser.roles.length; i++) { - if (authUser.roles[i].toLowerCase() === constants.UserRoles.Admin.toLowerCase()) { - return true + if ( + authUser.roles[i].toLowerCase() === + constants.UserRoles.Admin.toLowerCase() + ) { + return true; } } } - return false + return false; } /** @@ -135,16 +150,18 @@ function hasAdminRole (authUser) { * @returns {Object} the new object with removed properties * @private */ -function _sanitizeObject (obj) { +function _sanitizeObject(obj) { try { - return JSON.parse(JSON.stringify(obj, (name, value) => { - if (_.isArray(value) && value.length > 30) { - return `Array(${value.length})` - } - return value - })) + return JSON.parse( + JSON.stringify(obj, (name, value) => { + if (_.isArray(value) && value.length > 30) { + return `Array(${value.length})`; + } + return value; + }) + ); } catch (e) { - return obj + return obj; } } @@ -153,8 +170,8 @@ function _sanitizeObject (obj) { * @param {Object} obj the object * @returns {String} the string value */ -function toString (obj) { - return util.inspect(_sanitizeObject(obj), { breakLength: Infinity }) +function toString(obj) { + return util.inspect(_sanitizeObject(obj), { breakLength: Infinity }); } /** @@ -163,30 +180,30 @@ function toString (obj) { * @param {Array} source the array in which to search for the term * @param {Array | String} term the term to search */ -function checkIfExists (source, term) { - let terms +function checkIfExists(source, term) { + let terms; if (!_.isArray(source)) { - throw new Error('Source argument should be an array') + throw new Error("Source argument should be an array"); } - source = source.map(s => s.toLowerCase()) + source = source.map((s) => s.toLowerCase()); if (_.isString(term)) { - terms = term.toLowerCase().split(' ') + terms = term.toLowerCase().split(" "); } else if (_.isArray(term)) { - terms = term.map(t => t.toLowerCase()) + terms = term.map((t) => t.toLowerCase()); } else { - throw new Error('Term argument should be either a string or an array') + throw new Error("Term argument should be either a string or an array"); } for (let i = 0; i < terms.length; i++) { if (source.includes(terms[i])) { - return true + return true; } } - return false + return false; } /** @@ -195,19 +212,26 @@ function checkIfExists (source, term) { * @param {String} id The id value * @returns {Promise} */ -async function getById (modelName, id) { +async function getById(modelName, id) { return new Promise((resolve, reject) => { - models[modelName].query('id').eq(id).exec((err, result) => { - if (err) { - return reject(err) - } - if (result.length > 0) { - return resolve(result[0]) - } else { - return reject(new errors.NotFoundError(`${modelName} with id: ${id} doesn't exist`)) - } - }) - }) + models[modelName] + .query("id") + .eq(id) + .exec((err, result) => { + if (err) { + return reject(err); + } + if (result.length > 0) { + return resolve(result[0]); + } else { + return reject( + new errors.NotFoundError( + `${modelName} with id: ${id} doesn't exist` + ) + ); + } + }); + }); } /** @@ -216,13 +240,13 @@ async function getById (modelName, id) { * @param {Array} ids The ids * @returns {Promise} the found entities */ -async function getByIds (modelName, ids) { - const entities = [] - const theIds = ids || [] +async function getByIds(modelName, ids) { + const entities = []; + const theIds = ids || []; for (const id of theIds) { - entities.push(await getById(modelName, id)) + entities.push(await getById(modelName, id)); } - return entities + return entities; } /** @@ -232,11 +256,16 @@ async function getByIds (modelName, ids) { * @param {String} value The attribute value to be validated * @returns {Promise} */ -async function validateDuplicate (modelName, name, value) { - const list = await scan(modelName) +async function validateDuplicate(modelName, name, value) { + const list = await scan(modelName); for (let i = 0; i < list.length; i++) { - if (list[i][name] && String(list[i][name]).toLowerCase() === String(value).toLowerCase()) { - throw new errors.ConflictError(`${modelName} with ${name}: ${value} already exist`) + if ( + list[i][name] && + String(list[i][name]).toLowerCase() === String(value).toLowerCase() + ) { + throw new errors.ConflictError( + `${modelName} with ${name}: ${value} already exist` + ); } } } @@ -247,17 +276,17 @@ async function validateDuplicate (modelName, name, value) { * @param {Object} data The create data object * @returns {Promise} */ -async function create (modelName, data) { +async function create(modelName, data) { return new Promise((resolve, reject) => { - const dbItem = new models[modelName](data) + const dbItem = new models[modelName](data); dbItem.save((err) => { if (err) { - return reject(err) + return reject(err); } else { - return resolve(dbItem) + return resolve(dbItem); } - }) - }) + }); + }); } /** @@ -266,19 +295,19 @@ async function create (modelName, data) { * @param {Object} data The updated data object * @returns {Promise} */ -async function update (dbItem, data) { +async function update(dbItem, data) { Object.keys(data).forEach((key) => { - dbItem[key] = data[key] - }) + dbItem[key] = data[key]; + }); return new Promise((resolve, reject) => { dbItem.save((err) => { if (err) { - return reject(err) + return reject(err); } else { - return resolve(dbItem) + return resolve(dbItem); } - }) - }) + }); + }); } /** @@ -287,16 +316,16 @@ async function update (dbItem, data) { * @param {Object} scanParams The scan parameters object * @returns {Promise} */ -async function scan (modelName, scanParams) { +async function scan(modelName, scanParams) { return new Promise((resolve, reject) => { models[modelName].scan(scanParams).exec((err, result) => { if (err) { - return reject(err) + return reject(err); } else { - return resolve(result.count === 0 ? [] : result) + return resolve(result.count === 0 ? [] : result); } - }) - }) + }); + }); } /** @@ -305,15 +334,18 @@ async function scan (modelName, scanParams) { * @param {Object} scanParams The scan parameters object * @returns {Array} */ -async function scanAll (modelName, scanParams) { - let results = await models[modelName].scan(scanParams).exec() - let lastKey = results.lastKey +async function scanAll(modelName, scanParams) { + let results = await models[modelName].scan(scanParams).exec(); + let lastKey = results.lastKey; while (!_.isUndefined(results.lastKey)) { - const newResult = await models[modelName].scan(scanParams).startAt(lastKey).exec() - results = [...results, ...newResult] - lastKey = newResult.lastKey + const newResult = await models[modelName] + .scan(scanParams) + .startAt(lastKey) + .exec(); + results = [...results, ...newResult]; + lastKey = newResult.lastKey; } - return results + return results; } /** @@ -322,16 +354,16 @@ async function scanAll (modelName, scanParams) { * @param {String} value the value to test * @returns {Boolean} the match result */ -function partialMatch (filter, value) { +function partialMatch(filter, value) { if (filter) { if (value) { - const filtered = xss(filter) - return _.toLower(value).includes(_.toLower(filtered)) + const filtered = xss(filter); + return _.toLower(value).includes(_.toLower(filtered)); } else { - return false + return false; } } else { - return true + return true; } } @@ -339,18 +371,20 @@ function partialMatch (filter, value) { * Perform validation on phases. * @param {Array} phases the phases data. */ -async function validatePhases (phases) { +async function validatePhases(phases) { if (!phases || phases.length === 0) { - return + return; } - const records = await scan('Phase') - const map = new Map() - _.each(records, r => { - map.set(r.id, r) - }) - const invalidPhases = _.filter(phases, p => !map.has(p.phaseId)) + const records = await scan("Phase"); + const map = new Map(); + _.each(records, (r) => { + map.set(r.id, r); + }); + const invalidPhases = _.filter(phases, (p) => !map.has(p.phaseId)); if (invalidPhases.length > 0) { - throw new errors.BadRequestError(`The following phases are invalid: ${toString(invalidPhases)}`) + throw new errors.BadRequestError( + `The following phases are invalid: ${toString(invalidPhases)}` + ); } } @@ -360,12 +394,12 @@ async function validatePhases (phases) { * @param {String} key the key name * @return {Promise} promise resolved to downloaded data */ -async function downloadFromFileStack (url) { - const res = await axios.get(url) +async function downloadFromFileStack(url) { + const res = await axios.get(url); return { data: res.data, - mimetype: _.get(res, `headers['content-type']`, 'application/json') - } + mimetype: _.get(res, `headers['content-type']`, "application/json"), + }; } /** @@ -374,12 +408,12 @@ async function downloadFromFileStack (url) { * @param {String} key the key name * @return {Promise} promise resolved to downloaded data */ -async function downloadFromS3 (bucket, key) { - const file = await s3.getObject({ Bucket: bucket, Key: key }).promise() +async function downloadFromS3(bucket, key) { + const file = await s3.getObject({ Bucket: bucket, Key: key }).promise(); return { data: file.Body, - mimetype: file.ContentType - } + mimetype: file.ContentType, + }; } /** @@ -388,16 +422,19 @@ async function downloadFromS3 (bucket, key) { * @param {String} key the key name * @return {Promise} promise resolved to deleted data */ -async function deleteFromS3 (bucket, key) { - return s3.deleteObject({ Bucket: bucket, Key: key }).promise() +async function deleteFromS3(bucket, key) { + return s3.deleteObject({ Bucket: bucket, Key: key }).promise(); } /** * Get M2M token. * @returns {Promise} the M2M token */ -async function getM2MToken () { - return m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) +async function getM2MToken() { + return m2m.getMachineToken( + config.AUTH0_CLIENT_ID, + config.AUTH0_CLIENT_SECRET + ); } /** @@ -405,24 +442,29 @@ async function getM2MToken () { * @param {String} challengeId the challenge id * @returns {Promise} the challenge resources */ -async function getChallengeResources (challengeId) { - const token = await getM2MToken() - const perPage = 100 - let page = 1 - let result = [] +async function getChallengeResources(challengeId) { + const token = await getM2MToken(); + const perPage = 100; + let page = 1; + let result = []; while (true) { - const url = `${config.RESOURCES_API_URL}?challengeId=${challengeId}&perPage=${perPage}&page=${page}` - const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }) + const url = `${config.RESOURCES_API_URL}?challengeId=${challengeId}&perPage=${perPage}&page=${page}`; + const res = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + }); if (!res.data || res.data.length === 0) { - break + break; } - result = result.concat(res.data) - page += 1 - if (res.headers['x-total-pages'] && page > Number(res.headers['x-total-pages'])) { - break + result = result.concat(res.data); + page += 1; + if ( + res.headers["x-total-pages"] && + page > Number(res.headers["x-total-pages"]) + ) { + break; } } - return result + return result; } /** @@ -431,17 +473,19 @@ async function getChallengeResources (challengeId) { * @param {String} memberHandle the user's member handle * @param {String} roleId the resource role ID to assign */ -async function createResource (challengeId, memberHandle, roleId) { - const token = await getM2MToken() +async function createResource(challengeId, memberHandle, roleId) { + const token = await getM2MToken(); const userObj = { challengeId, memberHandle, - roleId - } - const url = `${config.RESOURCES_API_URL}` - const res = await axios.post(url, userObj, { headers: { Authorization: `Bearer ${token}` } }) - return res || false + roleId, + }; + const url = `${config.RESOURCES_API_URL}`; + const res = await axios.post(url, userObj, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res || false; } /** @@ -452,73 +496,94 @@ async function createResource (challengeId, memberHandle, roleId) { * @param {String} token The token * @returns */ -async function createSelfServiceProject (name, description, type, token) { +async function createSelfServiceProject(name, description, type, token) { const projectObj = { name, description, - type - } - const url = `${config.PROJECTS_API_URL}` - const res = await axios.post(url, projectObj, { headers: { Authorization: `Bearer ${token}` } }) - return _.get(res, 'data.id') + type, + }; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); + const url = `${config.PROJECTS_API_URL}`; + const res = await axios.post(url, projectObj, { + headers: { Authorization: `Bearer ${token}` }, + }); + return _.get(res, "data.id"); } /** * Get project id by roundId * @param {String} roundId the round id */ -async function getProjectIdByRoundId (roundId) { - const url = `${config.CHALLENGE_MIGRATION_APP_URL}/getChallengeProjectId/${roundId}` - const res = await axios.get(url) - return _.get(res, 'data.projectId') +async function getProjectIdByRoundId(roundId) { + const url = `${config.CHALLENGE_MIGRATION_APP_URL}/getChallengeProjectId/${roundId}`; + const res = await axios.get(url); + return _.get(res, "data.projectId"); } /** * Get project payment * @param {String} projectId the project id */ -async function getProjectPayment (projectId) { - const token = await getM2MToken() - const url = `${config.CUSTOMER_PAYMENTS_URL}` +async function getProjectPayment(projectId) { + const token = await getM2MToken(); + const url = `${config.CUSTOMER_PAYMENTS_URL}`; const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` }, params: { referenceId: projectId, - reference: 'project' - } - }) - const [payment] = res.data - return payment + reference: "project", + }, + }); + const [payment] = res.data; + return payment; } /** * Charge payment * @param {String} paymentId the payment ID */ -async function capturePayment (paymentId) { - const token = await getM2MToken() - const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/charge` - logger.info(`Calling: ${url} to capture payment`) - const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }) - logger.debug(`Payment API Response: ${JSON.stringify(res.data, null, 2)}`) - if (res.data.status !== 'succeeded') { - throw new Error(`Failed to charge payment. Current status: ${res.data.status}`) +async function capturePayment(paymentId) { + const token = await getM2MToken(); + const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/charge`; + logger.info(`Calling: ${url} to capture payment`); + const res = await axios.patch( + url, + {}, + { headers: { Authorization: `Bearer ${token}` } } + ); + logger.debug(`Payment API Response: ${JSON.stringify(res.data, null, 2)}`); + if (res.data.status !== "succeeded") { + throw new Error( + `Failed to charge payment. Current status: ${res.data.status}` + ); } - return res.data + return res.data; } /** * Cancel payment * @param {String} paymentId the payment ID */ -async function cancelPayment (paymentId) { - const token = await getM2MToken() - const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/cancel` - const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }) - if (res.data.status !== 'canceled') { - throw new Error(`Failed to cancel payment. Current status: ${res.data.status}`) +async function cancelPayment(paymentId) { + const token = await getM2MToken(); + const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/cancel`; + const res = await axios.patch( + url, + {}, + { headers: { Authorization: `Bearer ${token}` } } + ); + if (res.data.status !== "canceled") { + throw new Error( + `Failed to cancel payment. Current status: ${res.data.status}` + ); } - return res.data + return res.data; } /** @@ -527,30 +592,41 @@ async function cancelPayment (paymentId) { * @param {String} cancelReason the cancel reasonn * @param {Object} currentUser the current user */ -async function cancelProject (projectId, cancelReason, currentUser) { - let payment = await getProjectPayment(projectId) - const project = await ensureProjectExist(projectId, currentUser) - if (project.status === 'cancelled') return // already canceled +async function cancelProject(projectId, cancelReason, currentUser) { + let payment = await getProjectPayment(projectId); + const project = await ensureProjectExist(projectId, currentUser); + if (project.status === "cancelled") return; // already canceled try { - payment = await cancelPayment(payment.id) + payment = await cancelPayment(payment.id); } catch (e) { - logger.debug(`Failed to cancel payment with error: ${e.message}`) + logger.debug(`Failed to cancel payment with error: ${e.message}`); } - const token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - await axios.patch(url, { - cancelReason, - status: 'cancelled', - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status - } - }, { headers: { Authorization: `Bearer ${token}` } }) + const token = await getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); + await axios.patch( + url, + { + cancelReason, + status: "cancelled", + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + }, + }, + { headers: { Authorization: `Bearer ${token}` } } + ); } /** @@ -558,41 +634,60 @@ async function cancelProject (projectId, cancelReason, currentUser) { * @param {String} projectId the project id * @param {Object} currentUser the current user */ -async function activateProject (projectId, currentUser, name, description) { - let payment - let project +async function activateProject(projectId, currentUser, name, description) { + let payment; + let project; try { - payment = await getProjectPayment(projectId) - project = await ensureProjectExist(projectId, currentUser) - if (payment.status !== 'succeeded') { - payment = await capturePayment(payment.id) + payment = await getProjectPayment(projectId); + project = await ensureProjectExist(projectId, currentUser); + if (payment.status !== "succeeded") { + payment = await capturePayment(payment.id); } } catch (e) { - logger.debug(e) - logger.debug(`Failed to charge payment ${payment.id} with error: ${e.message}`) - await cancelProject(projectId, `Failed to charge payment ${payment.id} with error: ${e.message}`, currentUser) - throw new Error(`Failed to charge payment ${payment.id} with error: ${e.message}`) + logger.debug(e); + logger.debug( + `Failed to charge payment ${payment.id} with error: ${e.message}` + ); + await cancelProject( + projectId, + `Failed to charge payment ${payment.id} with error: ${e.message}`, + currentUser + ); + throw new Error( + `Failed to charge payment ${payment.id} with error: ${e.message}` + ); } - const token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - const res = await axios.patch(url, { - name, - description, - status: 'active', - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status - } - }, { headers: { Authorization: `Bearer ${token}` } }) - - if (res.data && res.data.status === 'reviewed') { + const token = await getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); + const res = await axios.patch( + url, + { + name, + description, + status: "active", + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + }, + }, + { headers: { Authorization: `Bearer ${token}` } } + ); + + if (res.data && res.data.status === "reviewed") { // auto activate if the project goes in reviewed state - await activateProject(projectId, currentUser, name, description) + await activateProject(projectId, currentUser, name, description); } } @@ -602,33 +697,50 @@ async function activateProject (projectId, currentUser, name, description) { * @param {*} workItemPlannedEndDate the planned end date of the work item * @param {*} currentUser the current user */ -async function updateSelfServiceProjectInfo (projectId, workItemPlannedEndDate, currentUser) { - const project = await ensureProjectExist(projectId, currentUser) - const payment = await getProjectPayment(projectId) - const token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` - const res = await axios.patch(url, { - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status, - workItemPlannedEndDate - } - }, { headers: { Authorization: `Bearer ${token}` } }) +async function updateSelfServiceProjectInfo( + projectId, + workItemPlannedEndDate, + currentUser +) { + const project = await ensureProjectExist(projectId, currentUser); + const payment = await getProjectPayment(projectId); + const token = await getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); + const res = await axios.patch( + url, + { + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + workItemPlannedEndDate, + }, + }, + { headers: { Authorization: `Bearer ${token}` } } + ); } /** * Get resource roles * @returns {Promise} the challenge resources */ -async function getResourceRoles () { - const token = await getM2MToken() - const res = await axios.get(config.RESOURCE_ROLES_API_URL, { headers: { Authorization: `Bearer ${token}` } }) - return res.data || [] +async function getResourceRoles() { + const token = await getM2MToken(); + const res = await axios.get(config.RESOURCE_ROLES_API_URL, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data || []; } /** @@ -636,11 +748,21 @@ async function getResourceRoles () { * @param {String} challengeId the challenge UUID * @param {String} userId the user ID */ -async function userHasFullAccess (challengeId, userId) { - const resourceRoles = await getResourceRoles() - const rolesWithFullAccess = _.map(_.filter(resourceRoles, r => r.fullWriteAccess), 'id') - const challengeResources = await getChallengeResources(challengeId) - return _.filter(challengeResources, r => _.toString(r.memberId) === _.toString(userId) && _.includes(rolesWithFullAccess, r.roleId)).length > 0 +async function userHasFullAccess(challengeId, userId) { + const resourceRoles = await getResourceRoles(); + const rolesWithFullAccess = _.map( + _.filter(resourceRoles, (r) => r.fullWriteAccess), + "id" + ); + const challengeResources = await getChallengeResources(challengeId); + return ( + _.filter( + challengeResources, + (r) => + _.toString(r.memberId) === _.toString(userId) && + _.includes(rolesWithFullAccess, r.roleId) + ).length > 0 + ); } /** @@ -648,11 +770,11 @@ async function userHasFullAccess (challengeId, userId) { * @param {String} userId the user id * @returns {Promise} the user groups */ -async function getUserGroups (userId) { - const token = await getM2MToken() - let allGroups = [] +async function getUserGroups(userId) { + const token = await getM2MToken(); + let allGroups = []; // get search is paginated, we need to get all pages' data - let page = 1 + let page = 1; while (true) { const result = await axios.get(config.GROUPS_API_URL, { headers: { Authorization: `Bearer ${token}` }, @@ -660,20 +782,23 @@ async function getUserGroups (userId) { page, perPage: 5000, memberId: userId, - membershipType: 'user' - } - }) - const groups = result.data || [] + membershipType: "user", + }, + }); + const groups = result.data || []; if (groups.length === 0) { - break + break; } - allGroups = allGroups.concat(groups) - page += 1 - if (result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { - break + allGroups = allGroups.concat(groups); + page += 1; + if ( + result.headers["x-total-pages"] && + page > Number(result.headers["x-total-pages"]) + ) { + break; } } - return allGroups + return allGroups; } /** @@ -681,16 +806,19 @@ async function getUserGroups (userId) { * @param {String} userId the user id * @returns {Promise} the user groups */ -async function getCompleteUserGroupTreeIds (userId) { - const token = await getM2MToken() - const result = await axios.get(`${config.GROUPS_API_URL}/memberGroups/${userId}`, { - headers: { Authorization: `Bearer ${token}` }, - params: { - uuid: true +async function getCompleteUserGroupTreeIds(userId) { + const token = await getM2MToken(); + const result = await axios.get( + `${config.GROUPS_API_URL}/memberGroups/${userId}`, + { + headers: { Authorization: `Bearer ${token}` }, + params: { + uuid: true, + }, } - }) + ); - return result.data || [] + return result.data || []; } /** @@ -698,16 +826,16 @@ async function getCompleteUserGroupTreeIds (userId) { * @param {String} groupId the group ID * @returns {Array} an array with the groups ID and the IDs of all subGroups */ -async function expandWithSubGroups (groupId) { - const token = await getM2MToken() +async function expandWithSubGroups(groupId) { + const token = await getM2MToken(); const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { headers: { Authorization: `Bearer ${token}` }, params: { - includeSubGroups: true - } - }) - const groups = result.data || {} - return [groupId, ..._.map(_.get(groups, 'subGroups', []), 'id')] + includeSubGroups: true, + }, + }); + const groups = result.data || {}; + return [groupId, ..._.map(_.get(groups, "subGroups", []), "id")]; } /** @@ -715,26 +843,26 @@ async function expandWithSubGroups (groupId) { * @param {String} groupId the group ID * @returns {Array} an array with the group ID and the IDs of all parent groups up the chain */ -async function expandWithParentGroups (groupId) { - const token = await getM2MToken() +async function expandWithParentGroups(groupId) { + const token = await getM2MToken(); const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { headers: { Authorization: `Bearer ${token}` }, params: { includeParentGroup: true, - oneLevel: false - } - }) + oneLevel: false, + }, + }); - const ids = [] + const ids = []; const extractIds = (group) => { - ids.push(group.id) - _.each(_.get(group, 'parentGroups', []), (parent) => { - extractIds(parent) - }) - } - - extractIds(result.data || {}) - return ids + ids.push(group.id); + _.each(_.get(group, "parentGroups", []), (parent) => { + extractIds(parent); + }); + }; + + extractIds(result.data || {}); + return ids; } /** @@ -742,15 +870,17 @@ async function expandWithParentGroups (groupId) { * @param {Array} arr the array to check * @param {String} name the array name */ -function ensureNoDuplicateOrNullElements (arr, name) { - const a = arr || [] +function ensureNoDuplicateOrNullElements(arr, name) { + const a = arr || []; for (let i = 0; i < a.length; i += 1) { if (_.isNil(a[i])) { - throw new errors.BadRequestError(`There is null element for ${name}.`) + throw new errors.BadRequestError(`There is null element for ${name}.`); } for (let j = i + 1; j < a.length; j += 1) { if (a[i] === a[j]) { - throw new errors.BadRequestError(`There are duplicate elements (${a[i]}) for ${name}.`) + throw new errors.BadRequestError( + `There are duplicate elements (${a[i]}) for ${name}.` + ); } } } @@ -760,16 +890,24 @@ function ensureNoDuplicateOrNullElements (arr, name) { * Get Bus API Client * @return {Object} Bus API Client Instance */ -function getBusApiClient () { +function getBusApiClient() { // if there is no bus API client instance, then create a new instance if (!busApiClient) { - busApiClient = busApi(_.pick(config, - ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', - 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET', 'BUSAPI_URL', - 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) + busApiClient = busApi( + _.pick(config, [ + "AUTH0_URL", + "AUTH0_AUDIENCE", + "TOKEN_CACHE_TIME", + "AUTH0_CLIENT_ID", + "AUTH0_CLIENT_SECRET", + "BUSAPI_URL", + "KAFKA_ERROR_TOPIC", + "AUTH0_PROXY_SERVER_URL", + ]) + ); } - return busApiClient + return busApiClient; } /** @@ -778,48 +916,48 @@ function getBusApiClient () { * @param {Object} payload the event payload * @param {Object} options the extra options to the message */ -async function postBusEvent (topic, payload, options = {}) { - const client = getBusApiClient() +async function postBusEvent(topic, payload, options = {}) { + const client = getBusApiClient(); const message = { topic, originator: constants.EVENT_ORIGINATOR, timestamp: new Date().toISOString(), - 'mime-type': constants.EVENT_MIME_TYPE, - payload - } + "mime-type": constants.EVENT_MIME_TYPE, + payload, + }; if (options.key) { - message.key = options.key + message.key = options.key; } - await client.postEvent(message) + await client.postEvent(message); } /** * Get ES Client * @return {Object} Elasticsearch Client Instance */ -function getESClient () { +function getESClient() { if (esClient) { - return esClient + return esClient; } - const esHost = config.get('ES.HOST') + const esHost = config.get("ES.HOST"); // AWS ES configuration is different from other providers if (/.*amazonaws.*/.test(esHost)) { esClient = elasticsearch.Client({ - apiVersion: config.get('ES.API_VERSION'), + apiVersion: config.get("ES.API_VERSION"), hosts: esHost, - connectionClass: require('http-aws-es'), // eslint-disable-line global-require + connectionClass: require("http-aws-es"), // eslint-disable-line global-require amazonES: { - region: config.get('AMAZON.AWS_REGION'), - credentials: new AWS.EnvironmentCredentials('AWS') - } - }) + region: config.get("AMAZON.AWS_REGION"), + credentials: new AWS.EnvironmentCredentials("AWS"), + }, + }); } else { esClient = new elasticsearch.Client({ - apiVersion: config.get('ES.API_VERSION'), - hosts: esHost - }) + apiVersion: config.get("ES.API_VERSION"), + hosts: esHost, + }); } - return esClient + return esClient; } /** @@ -827,27 +965,51 @@ function getESClient () { * @param {String} projectId the project id * @param {String} currentUser the user */ -async function ensureProjectExist (projectId, currentUser) { - let token = await getM2MToken() - const url = `${config.PROJECTS_API_URL}/${projectId}` +async function ensureProjectExist(projectId, currentUser) { + let token = await getM2MToken(); + const url = `${config.PROJECTS_API_URL}/${projectId}`; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); try { - const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }) + const res = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + }); if (currentUser.isMachine || hasAdminRole(currentUser)) { - return res.data + return res.data; } - if (_.get(res, 'data.type') === 'self-service' && _.includes(config.SELF_SERVICE_WHITELIST_HANDLES, currentUser.handle.toLowerCase())) { - return res.data + if ( + _.get(res, "data.type") === "self-service" && + _.includes( + config.SELF_SERVICE_WHITELIST_HANDLES, + currentUser.handle.toLowerCase() + ) + ) { + return res.data; } - if (!_.find(_.get(res, 'data.members', []), m => _.toString(m.userId) === _.toString(currentUser.userId))) { - throw new errors.ForbiddenError(`You don't have access to project with ID: ${projectId}`) + if ( + !_.find( + _.get(res, "data.members", []), + (m) => _.toString(m.userId) === _.toString(currentUser.userId) + ) + ) { + throw new errors.ForbiddenError( + `You don't have access to project with ID: ${projectId}` + ); } - return res.data + return res.data; } catch (err) { - if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`) + if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError( + `Project with id: ${projectId} doesn't exist` + ); } else { // re-throw other error - throw err + throw err; } } } @@ -856,12 +1018,12 @@ async function ensureProjectExist (projectId, currentUser) { * Calculates challenge end date based on its phases * @param {any} challenge */ -function calculateChallengeEndDate (challenge, data) { +function calculateChallengeEndDate(challenge, data) { if (!data) { - data = challenge + data = challenge; } - let lastPhase = data.phases[data.phases.length - 1] - return lastPhase.actualEndDate || lastPhase.scheduledEndDate + let lastPhase = data.phases[data.phases.length - 1]; + return lastPhase.actualEndDate || lastPhase.scheduledEndDate; // let phase = data.phases[data.phases.length - 1] // if (!phase || (!data.startDate && !challenge.startDate)) { // return data.startDate || challenge.startDate @@ -883,36 +1045,46 @@ function calculateChallengeEndDate (challenge, data) { * @param {Number} memberId the member id * @returns {Promise} an array of challenge ids represents challenges that given member has access to. */ -async function listChallengesByMember (memberId) { - const token = await getM2MToken() - let allIds = [] +async function listChallengesByMember(memberId) { + const token = await getM2MToken(); + let allIds = []; // get search is paginated, we need to get all pages' data - let page = 1 + let page = 1; while (true) { - let result = {} + let result = {}; try { - result = await axios.get(`${config.RESOURCES_API_URL}/${memberId}/challenges`, { - headers: { Authorization: `Bearer ${token}` }, - params: { - page, - perPage: 10000 + result = await axios.get( + `${config.RESOURCES_API_URL}/${memberId}/challenges`, + { + headers: { Authorization: `Bearer ${token}` }, + params: { + page, + perPage: 10000, + }, } - }) + ); } catch (e) { // only log the error but don't throw it, so the following logic can still be executed. - logger.debug(`Failed to get challenges that accessible to the memberId ${memberId}`, e) + logger.debug( + `Failed to get challenges that accessible to the memberId ${memberId}`, + e + ); } - const ids = result.data || [] + const ids = result.data || []; if (ids.length === 0) { - break + break; } - allIds = allIds.concat(ids) - page += 1 - if (result.headers && result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { - break + allIds = allIds.concat(ids); + page += 1; + if ( + result.headers && + result.headers["x-total-pages"] && + page > Number(result.headers["x-total-pages"]) + ) { + break; } } - return allIds + return allIds; } /** @@ -921,9 +1093,12 @@ async function listChallengesByMember (memberId) { * @param {String} method method to be tested * @returns {String} method valid method */ -async function validateESRefreshMethod (method) { - Joi.attempt(method, Joi.string().label('ES_REFRESH').valid(['true', 'false', 'wait_for'])) - return method +async function validateESRefreshMethod(method) { + Joi.attempt( + method, + Joi.string().label("ES_REFRESH").valid(["true", "false", "wait_for"]) + ); + return method; } /** @@ -932,18 +1107,29 @@ async function validateESRefreshMethod (method) { * @param {Number} projectId The id of the project for which to get the default terms of use * @returns {Promise>} An array containing the ids of the default project terms of use */ -async function getProjectDefaultTerms (projectId) { - const token = await getM2MToken() - const projectUrl = `${config.PROJECTS_API_URL}/${projectId}` +async function getProjectDefaultTerms(projectId) { + const token = await getM2MToken(); + const projectUrl = `${config.PROJECTS_API_URL}/${projectId}`; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); try { - const res = await axios.get(projectUrl, { headers: { Authorization: `Bearer ${token}` } }) - return res.data.terms || [] + const res = await axios.get(projectUrl, { + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data.terms || []; } catch (err) { - if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`) + if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError( + `Project with id: ${projectId} doesn't exist` + ); } else { // re-throw other error - throw err + throw err; } } } @@ -954,29 +1140,40 @@ async function getProjectDefaultTerms (projectId) { * @param {Number} projectId The id of the project for which to get the default terms of use * @returns {Promise} The billing account ID */ -async function getProjectBillingInformation (projectId) { - const token = await getM2MToken() - const projectUrl = `${config.PROJECTS_API_URL}/${projectId}/billingAccount` +async function getProjectBillingInformation(projectId) { + const token = await getM2MToken(); + const projectUrl = `${config.PROJECTS_API_URL}/${projectId}/billingAccount`; + axiosRetry(axios, { + retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries + retryCondition: (e) => { + return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; + }, + retryDelay: axiosRetry.exponentialDelay + }); try { - const res = await axios.get(projectUrl, { headers: { Authorization: `Bearer ${token}` } }) - let markup = _.get(res, 'data.markup', null) ? _.toNumber(_.get(res, 'data.markup', null)) : null + const res = await axios.get(projectUrl, { + headers: { Authorization: `Bearer ${token}` }, + }); + let markup = _.get(res, "data.markup", null) + ? _.toNumber(_.get(res, "data.markup", null)) + : null; if (markup && markup > 0) { // TODO - Hack to change int returned from api to decimal - markup = markup / 100 + markup = markup / 100; } return { - billingAccountId: _.get(res, 'data.tcBillingAccountId', null), - markup - } + billingAccountId: _.get(res, "data.tcBillingAccountId", null), + markup, + }; } catch (err) { - if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { + if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { return { billingAccountId: null, - markup: null - } + markup: null, + }; } else { // re-throw other error - throw err + throw err; } } } @@ -987,25 +1184,29 @@ async function getProjectBillingInformation (projectId) { * * @param {Array} terms The array of terms {id, roleId} to retrieve from terms API */ -async function validateChallengeTerms (terms = []) { - const listOfTerms = [] - const token = await getM2MToken() +async function validateChallengeTerms(terms = []) { + const listOfTerms = []; + const token = await getM2MToken(); for (let term of terms) { // Get the terms details from the API try { - await axios.get(`${config.TERMS_API_URL}/${term.id}`, { headers: { Authorization: `Bearer ${token}` } }) - listOfTerms.push(term) + await axios.get(`${config.TERMS_API_URL}/${term.id}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + listOfTerms.push(term); } catch (e) { - if (_.get(e, 'response.status') === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError(`Terms of use identified by the id ${term.id} does not exist`) + if (_.get(e, "response.status") === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError( + `Terms of use identified by the id ${term.id} does not exist` + ); } else { // re-throw other error - throw e + throw e; } } } - return listOfTerms + return listOfTerms; } /** @@ -1014,28 +1215,42 @@ async function validateChallengeTerms (terms = []) { * @param {Array} challenges the challenges to filter * @returns {Array} the challenges that can be accessed by current user */ -async function _filterChallengesByGroupsAccess (currentUser, challenges) { - const res = [] - const needToCheckForGroupAccess = !currentUser ? true : !currentUser.isMachine && !hasAdminRole(currentUser) - if (!needToCheckForGroupAccess) return challenges +async function _filterChallengesByGroupsAccess(currentUser, challenges) { + const res = []; + const needToCheckForGroupAccess = !currentUser + ? true + : !currentUser.isMachine && !hasAdminRole(currentUser); + if (!needToCheckForGroupAccess) return challenges; - let userGroups + let userGroups; for (const challenge of challenges) { - challenge.groups = _.filter(challenge.groups, g => !_.includes(['null', 'undefined'], _.toString(g).toLowerCase())) - if (!challenge.groups || _.get(challenge, 'groups.length', 0) === 0 || !needToCheckForGroupAccess) { - res.push(challenge) + challenge.groups = _.filter( + challenge.groups, + (g) => !_.includes(["null", "undefined"], _.toString(g).toLowerCase()) + ); + if ( + !challenge.groups || + _.get(challenge, "groups.length", 0) === 0 || + !needToCheckForGroupAccess + ) { + res.push(challenge); } else if (currentUser) { if (_.isNil(userGroups)) { - userGroups = await getCompleteUserGroupTreeIds(currentUser.userId) + userGroups = await getCompleteUserGroupTreeIds(currentUser.userId); } // get user groups if not yet - if (_.find(challenge.groups, (group) => !!_.find(userGroups, (ug) => ug === group))) { - res.push(challenge) + if ( + _.find( + challenge.groups, + (group) => !!_.find(userGroups, (ug) => ug === group) + ) + ) { + res.push(challenge); } } } - return res + return res; } /** @@ -1043,10 +1258,14 @@ async function _filterChallengesByGroupsAccess (currentUser, challenges) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function ensureAccessibleByGroupsAccess (currentUser, challenge) { - const filtered = await _filterChallengesByGroupsAccess(currentUser, [challenge]) +async function ensureAccessibleByGroupsAccess(currentUser, challenge) { + const filtered = await _filterChallengesByGroupsAccess(currentUser, [ + challenge, + ]); if (filtered.length === 0) { - throw new errors.ForbiddenError("helper ensureAcessibilityToModifiedGroups :: You don't have access to this group!") + throw new errors.ForbiddenError( + "helper ensureAcessibilityToModifiedGroups :: You don't have access to this group!" + ); } } @@ -1056,19 +1275,30 @@ async function ensureAccessibleByGroupsAccess (currentUser, challenge) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function _ensureAccessibleForTaskChallenge (currentUser, challenge) { - let challengeResourceIds +async function _ensureAccessibleForTaskChallenge(currentUser, challenge) { + let challengeResourceIds; // Check if challenge is task and apply security rules - if (_.get(challenge, 'task.isTask', false) && _.get(challenge, 'task.isAssigned', false)) { + if ( + _.get(challenge, "task.isTask", false) && + _.get(challenge, "task.isAssigned", false) + ) { if (currentUser) { if (!currentUser.isMachine) { - const challengeResources = await getChallengeResources(challenge.id) - challengeResourceIds = _.map(challengeResources, r => _.toString(r.memberId)) + const challengeResources = await getChallengeResources(challenge.id); + challengeResourceIds = _.map(challengeResources, (r) => + _.toString(r.memberId) + ); } } - const canAccesChallenge = _.isUndefined(currentUser) ? false : currentUser.isMachine || hasAdminRole(currentUser) || _.includes((challengeResourceIds || []), _.toString(currentUser.userId)) + const canAccesChallenge = _.isUndefined(currentUser) + ? false + : currentUser.isMachine || + hasAdminRole(currentUser) || + _.includes(challengeResourceIds || [], _.toString(currentUser.userId)); if (!canAccesChallenge) { - throw new errors.ForbiddenError(`You don't have access to view this challenge`) + throw new errors.ForbiddenError( + `You don't have access to view this challenge` + ); } } } @@ -1079,11 +1309,11 @@ async function _ensureAccessibleForTaskChallenge (currentUser, challenge) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function ensureUserCanViewChallenge (currentUser, challenge) { +async function ensureUserCanViewChallenge(currentUser, challenge) { // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge) + await ensureAccessibleByGroupsAccess(currentUser, challenge); // check if user can access a challenge that is a task - await _ensureAccessibleForTaskChallenge(currentUser, challenge) + await _ensureAccessibleForTaskChallenge(currentUser, challenge); } /** @@ -1093,50 +1323,60 @@ async function ensureUserCanViewChallenge (currentUser, challenge) { * @param {Object} challenge the challenge to check * @returns {undefined} */ -async function ensureUserCanModifyChallenge (currentUser, challenge) { +async function ensureUserCanModifyChallenge(currentUser, challenge) { // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge) + await ensureAccessibleByGroupsAccess(currentUser, challenge); // check full access - const isUserHasFullAccess = await userHasFullAccess(challenge.id, currentUser.userId) - if (!currentUser.isMachine && !hasAdminRole(currentUser) && challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && !isUserHasFullAccess) { - throw new errors.ForbiddenError(`Only M2M, admin, challenge's copilot or users with full access can perform modification.`) + const isUserHasFullAccess = await userHasFullAccess( + challenge.id, + currentUser.userId + ); + if ( + !currentUser.isMachine && + !hasAdminRole(currentUser) && + challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && + !isUserHasFullAccess + ) { + throw new errors.ForbiddenError( + `Only M2M, admin, challenge's copilot or users with full access can perform modification.` + ); } } /** - * Calculate the sum of prizes. - * - * @param {Array} prizes the list of prize - * @returns {Number} the result prize - */ -function sumOfPrizes (prizes) { - let sum = 0 + * Calculate the sum of prizes. + * + * @param {Array} prizes the list of prize + * @returns {Number} the result prize + */ +function sumOfPrizes(prizes) { + let sum = 0; if (!prizes.length) { - return sum + return sum; } for (const prize of prizes) { - sum += prize.value + sum += prize.value; } - return sum + return sum; } /** - * Get group by id - * @param {String} groupId the group id - * @returns {Promise} the group - */ -async function getGroupById (groupId) { - const token = await getM2MToken() + * Get group by id + * @param {String} groupId the group id + * @returns {Promise} the group + */ +async function getGroupById(groupId) { + const token = await getM2MToken(); try { const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { - headers: { Authorization: `Bearer ${token}` } - }) - return result.data + headers: { Authorization: `Bearer ${token}` }, + }); + return result.data; } catch (err) { if (err.response.status === HttpStatus.NOT_FOUND) { - return + return; } - throw err + throw err; } } @@ -1145,30 +1385,36 @@ async function getGroupById (groupId) { * @param {String} challengeId the challenge id * @returns {Array} the submission */ -async function getChallengeSubmissions (challengeId) { - const token = await getM2MToken() - let allSubmissions = [] +async function getChallengeSubmissions(challengeId) { + const token = await getM2MToken(); + let allSubmissions = []; // get search is paginated, we need to get all pages' data - let page = 1 + let page = 1; while (true) { - const result = await axios.get(`${config.SUBMISSIONS_API_URL}?challengeId=${challengeId}`, { - headers: { Authorization: `Bearer ${token}` }, - params: { - page, - perPage: 100 + const result = await axios.get( + `${config.SUBMISSIONS_API_URL}?challengeId=${challengeId}`, + { + headers: { Authorization: `Bearer ${token}` }, + params: { + page, + perPage: 100, + }, } - }) - const ids = result.data || [] + ); + const ids = result.data || []; if (ids.length === 0) { - break + break; } - allSubmissions = allSubmissions.concat(ids) - page += 1 - if (result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { - break + allSubmissions = allSubmissions.concat(ids); + page += 1; + if ( + result.headers["x-total-pages"] && + page > Number(result.headers["x-total-pages"]) + ) { + break; } } - return allSubmissions + return allSubmissions; } /** @@ -1176,13 +1422,13 @@ async function getChallengeSubmissions (challengeId) { * @param {String} userId the user ID * @returns {Object} */ -async function getMemberById (userId) { - const token = await getM2MToken() +async function getMemberById(userId) { + const token = await getM2MToken(); const res = await axios.get(`${config.MEMBERS_API_URL}?userId=${userId}`, { - headers: { Authorization: `Bearer ${token}` } - }) - if (res.data.length > 0) return res.data[0] - return {} + headers: { Authorization: `Bearer ${token}` }, + }); + if (res.data.length > 0) return res.data[0]; + return {}; } /** @@ -1190,12 +1436,12 @@ async function getMemberById (userId) { * @param {String} handle the user handle * @returns {Object} */ -async function getMemberByHandle (handle) { - const token = await getM2MToken() +async function getMemberByHandle(handle) { + const token = await getM2MToken(); const res = await axios.get(`${config.MEMBERS_API_URL}/${handle}`, { - headers: { Authorization: `Bearer ${token}` } - }) - return res.data || {} + headers: { Authorization: `Bearer ${token}` }, + }); + return res.data || {}; } /** @@ -1204,12 +1450,12 @@ async function getMemberByHandle (handle) { * @param {Array} recipients the array of recipients in { userId || email || handle } format * @param {Object} data the data */ -async function sendSelfServiceNotification (type, recipients, data) { +async function sendSelfServiceNotification(type, recipients, data) { try { await postBusEvent(constants.Topics.Notifications, { notifications: [ { - serviceId: 'email', + serviceId: "email", type, details: { from: config.EMAIL_FROM, @@ -1217,16 +1463,18 @@ async function sendSelfServiceNotification (type, recipients, data) { cc: [...constants.SelfServiceNotificationSettings[type].cc], data: { ...data, - supportUrl: `${config.SELF_SERVICE_APP_URL}/support` + supportUrl: `${config.SELF_SERVICE_APP_URL}/support`, }, - sendgridTemplateId: constants.SelfServiceNotificationSettings[type].sendgridTemplateId, - version: 'v3' - } - } - ] - }) + sendgridTemplateId: + constants.SelfServiceNotificationSettings[type] + .sendgridTemplateId, + version: "v3", + }, + }, + ], + }); } catch (e) { - logger.debug(`Failed to post notification ${type}: ${e.message}`) + logger.debug(`Failed to post notification ${type}: ${e.message}`); } } @@ -1234,31 +1482,35 @@ async function sendSelfServiceNotification (type, recipients, data) { * Submit a request to zendesk * @param {Object} request the request */ -async function submitZendeskRequest (request) { +async function submitZendeskRequest(request) { try { - const res = await axios.post(`${config.ZENDESK_API_URL}/api/v2/requests`, { - request: { - ...request - } - }, { - auth: { - username: `${request.requester.email}/token`, - password: config.ZENDESK_API_TOKEN + const res = await axios.post( + `${config.ZENDESK_API_URL}/api/v2/requests`, + { + request: { + ...request, + }, + }, + { + auth: { + username: `${request.requester.email}/token`, + password: config.ZENDESK_API_TOKEN, + }, } - }) - return res.data || {} + ); + return res.data || {}; } catch (e) { - logger.debug(`Failed to submit request: ${e.message}`) - throw e + logger.debug(`Failed to submit request: ${e.message}`); + throw e; } } -function getFromInternalCache (key) { - return internalCache.get(key) +function getFromInternalCache(key) { + return internalCache.get(key); } -function setToInternalCache (key, value) { - internalCache.set(key, value) +function setToInternalCache(key, value) { + internalCache.set(key, value); } module.exports = { @@ -1317,5 +1569,5 @@ module.exports = { submitZendeskRequest, updateSelfServiceProjectInfo, getFromInternalCache, - setToInternalCache -} + setToInternalCache, +}; From 10a215ea435b26616743b156b00af4599233f96a Mon Sep 17 00:00:00 2001 From: Himani Raghav Date: Fri, 13 Jan 2023 19:32:17 +0530 Subject: [PATCH 2/4] Revert "axios-retry added" This reverts commit c2badcbcb3710109ec0b87847ceb9311dbc7705a. --- config/default.js | 5 - package-lock.json | 149 ++--- package.json | 1 - src/common/helper.js | 1306 +++++++++++++++++------------------------- 4 files changed, 588 insertions(+), 873 deletions(-) diff --git a/config/default.js b/config/default.js index d33e027b..babe7a5e 100644 --- a/config/default.js +++ b/config/default.js @@ -98,11 +98,6 @@ module.exports = { WORK_COMPLETED: process.env.WORK_COMPLETED || '' }, - AXIOS_RETRY: { - RETRIES: process.env.RETRIES || 3, - RETRY_COUNT: process.env.RETRY_COUNT || 1 - }, - EMAIL_FROM: process.env.EMAIL_FROM || 'no-reply@topcoder.com', SELF_SERVICE_EMAIL_CC_ACCOUNTS: process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS ? _.map(process.env.SELF_SERVICE_EMAIL_CC_ACCOUNTS.split(','), email => ({ email })) : [{ email: 'sathya.jayabal@gmail.com' }], SELF_SERVICE_WHITELIST_HANDLES: process.env.SELF_SERVICE_WHITELIST_HANDLES ? process.env.SELF_SERVICE_WHITELIST_HANDLES.split(',') : ['TCConnCopilot', 'sstestcopilot'], diff --git a/package-lock.json b/package-lock.json index 8c0bc8cd..a70c4fae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,14 +112,6 @@ "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", "dev": true }, - "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, "@babel/template": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", @@ -556,15 +548,6 @@ "follow-redirects": "1.5.10" } }, - "axios-retry": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", - "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", - "requires": { - "@babel/runtime": "^7.15.4", - "is-retry-allowed": "^2.2.0" - } - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -883,7 +866,7 @@ "semver": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.1.tgz", - "integrity": "sha512-Ne6/HdGZvvpXBdjW3o8J0pvxC2jnmVNBK7MKkMgsOBfrsIdTXfA5x+H9DUbQ2xzyvnLv0A0v9x8R4B40xNZIRQ==" + "integrity": "sha1-n7P0AE+QDYPEeWj+QvdYPgWDLMk=" } } }, @@ -1524,7 +1507,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -1764,7 +1747,7 @@ "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, "requires": { "pify": "^2.0.0" @@ -1773,13 +1756,13 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, "requires": { "load-json-file": "^2.0.0", @@ -1790,7 +1773,7 @@ "read-pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, "requires": { "find-up": "^2.0.0", @@ -2624,7 +2607,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -2763,11 +2746,6 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, - "is-retry-allowed": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", - "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" - }, "is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -3192,7 +3170,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -3851,18 +3829,18 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, "path-parse": { @@ -3874,7 +3852,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "path-type": { "version": "3.0.0", @@ -3888,7 +3866,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true } } @@ -3902,7 +3880,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "4.0.1", @@ -3913,7 +3891,7 @@ "pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", "dev": true, "requires": { "find-up": "^2.0.0", @@ -3968,7 +3946,7 @@ "pkg-config": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw==", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", "dev": true, "requires": { "debug-log": "^1.0.0", @@ -3994,12 +3972,12 @@ "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, "process-nextick-args": { @@ -4041,7 +4019,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.8.0", @@ -4051,12 +4029,12 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { "version": "6.10.3", @@ -4069,7 +4047,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "queue-microtask": { "version": "1.2.3", @@ -4098,7 +4076,7 @@ "semver": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", - "integrity": "sha512-sfKXKhcz5XVyfUZa2V4RbjK0xjOJCMLNF9H4p4v0UCo9wNHM/lH9RDuyDbGEtxWLMDlPBc8xI7AbbVLKXty+rQ==" + "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU=" } } }, @@ -4127,7 +4105,7 @@ "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", "dev": true, "requires": { "load-json-file": "^4.0.0", @@ -4169,16 +4147,11 @@ "reconnect-core": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", - "integrity": "sha512-+gLKwmyRf2tjl6bLR03DoeWELzyN6LW9Xgr3vh7NXHHwPi0JC0N2TwPyf90oUEBkCRcD+bgQ+s3HORoG3nwHDg==", + "integrity": "sha1-+65SkZp4d9hE4yRtAaLyZwHIM8g=", "requires": { "backoff": "~2.5.0" } }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -4199,7 +4172,7 @@ "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", "dev": true, "requires": { "es6-error": "^4.0.1" @@ -4242,7 +4215,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", "dev": true }, "require-main-filename": { @@ -4254,7 +4227,7 @@ "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { "caller-path": "^0.1.0", @@ -4264,7 +4237,7 @@ "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", "dev": true } } @@ -4289,7 +4262,7 @@ "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { "onetime": "^2.0.0", @@ -4299,7 +4272,7 @@ "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "optional": true, "requires": { "glob": "^6.0.1" @@ -4353,7 +4326,7 @@ "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, "semver": { "version": "5.7.1", @@ -4416,7 +4389,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, "setprototypeof": { @@ -4427,7 +4400,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -4436,7 +4409,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, "side-channel": { @@ -4458,7 +4431,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "requires": { "is-arrayish": "^0.3.1" } @@ -4552,7 +4525,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.17.0", @@ -4573,7 +4546,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, "standard": { "version": "12.0.1", @@ -4681,7 +4654,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" } @@ -4689,13 +4662,13 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "superagent": { @@ -4718,7 +4691,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "supports-preserve-symlinks-flag": { "version": "1.0.0", @@ -4742,7 +4715,7 @@ "symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", "dev": true }, "table": { @@ -4840,13 +4813,13 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "tmp": { @@ -4861,7 +4834,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, "toidentifier": { @@ -4928,7 +4901,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { "safe-buffer": "^5.0.1" } @@ -4936,12 +4909,12 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -4977,13 +4950,13 @@ "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", "dev": true }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "uri-js": { "version": "4.4.1", @@ -5003,7 +4976,7 @@ "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -5012,12 +4985,12 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { "version": "3.4.0", @@ -5037,12 +5010,12 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -5074,7 +5047,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, "wide-align": { @@ -5194,12 +5167,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { "mkdirp": "^0.5.1" @@ -5228,7 +5201,7 @@ "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, "xss": { "version": "1.0.11", @@ -5254,7 +5227,7 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, "yamljs": { "version": "0.3.0", diff --git a/package.json b/package.json index 533a2cfe..3d2b8733 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "dependencies": { "aws-sdk": "^2.1145.0", "axios": "^0.19.0", - "axios-retry": "^3.3.1", "bluebird": "^3.5.1", "body-parser": "^1.15.1", "config": "^3.0.1", diff --git a/src/common/helper.js b/src/common/helper.js index 0e28f266..0db4a781 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -1,78 +1,75 @@ /** * This file defines helper methods */ -const Joi = require("joi"); -const _ = require("lodash"); -const querystring = require("querystring"); -const constants = require("../../app-constants"); -const models = require("../models"); -const errors = require("./errors"); -const util = require("util"); -const AWS = require("aws-sdk"); -const config = require("config"); -const m2mAuth = require("tc-core-library-js").auth.m2m; -const m2m = m2mAuth( - _.pick(config, ["AUTH0_URL", "AUTH0_AUDIENCE", "TOKEN_CACHE_TIME"]) -); -const axios = require("axios"); -const axiosRetry = require("axios-retry"); -const busApi = require("topcoder-bus-api-wrapper"); -const elasticsearch = require("elasticsearch"); -const NodeCache = require("node-cache"); -const HttpStatus = require("http-status-codes"); -const xss = require("xss"); -const logger = require("./logger"); +const Joi = require('joi') +const _ = require('lodash') +const querystring = require('querystring') +const constants = require('../../app-constants') +const models = require('../models') +const errors = require('./errors') +const util = require('util') +const AWS = require('aws-sdk') +const config = require('config') +const m2mAuth = require('tc-core-library-js').auth.m2m +const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME'])) +const axios = require('axios') +const busApi = require('topcoder-bus-api-wrapper') +const elasticsearch = require('elasticsearch') +const NodeCache = require('node-cache') +const HttpStatus = require('http-status-codes') +const xss = require('xss') +const logger = require('./logger') // Bus API Client -let busApiClient; +let busApiClient // Elasticsearch client -let esClient; +let esClient // validate ES refresh method -validateESRefreshMethod(config.ES.ES_REFRESH); +validateESRefreshMethod(config.ES.ES_REFRESH) AWS.config.update({ s3: config.AMAZON.S3_API_VERSION, // accessKeyId: config.AMAZON.AWS_ACCESS_KEY_ID, // secretAccessKey: config.AMAZON.AWS_SECRET_ACCESS_KEY, - region: config.AMAZON.AWS_REGION, -}); -const s3 = new AWS.S3(); + region: config.AMAZON.AWS_REGION +}) +const s3 = new AWS.S3() /** * Wrap async function to standard express function * @param {Function} fn the async function * @returns {Function} the wrapped function */ -function wrapExpress(fn) { +function wrapExpress (fn) { return function (req, res, next) { - fn(req, res, next).catch(next); - }; + fn(req, res, next).catch(next) + } } // Internal cache -const internalCache = new NodeCache({ stdTTL: config.INTERNAL_CACHE_TTL }); +const internalCache = new NodeCache({ stdTTL: config.INTERNAL_CACHE_TTL }) /** * Wrap all functions from object * @param obj the object (controller exports) * @returns {Object|Array} the wrapped object */ -function autoWrapExpress(obj) { +function autoWrapExpress (obj) { if (_.isArray(obj)) { - return obj.map(autoWrapExpress); + return obj.map(autoWrapExpress) } if (_.isFunction(obj)) { - if (obj.constructor.name === "AsyncFunction") { - return wrapExpress(obj); + if (obj.constructor.name === 'AsyncFunction') { + return wrapExpress(obj) } - return obj; + return obj } _.each(obj, (value, key) => { - obj[key] = autoWrapExpress(value); - }); - return obj; + obj[key] = autoWrapExpress(value) + }) + return obj } /** @@ -81,9 +78,9 @@ function autoWrapExpress(obj) { * @param {Number} page the page number * @returns {String} link for the page */ -function getPageLink(req, page) { - const q = _.assignIn({}, req.query, { page }); - return `${config.API_BASE_URL}${req.path}?${querystring.stringify(q)}`; +function getPageLink (req, page) { + const q = _.assignIn({}, req.query, { page }) + return `${config.API_BASE_URL}${req.path}?${querystring.stringify(q)}` } /** @@ -92,37 +89,28 @@ function getPageLink(req, page) { * @param {Object} res the HTTP response * @param {Object} result the operation result */ -function setResHeaders(req, res, result) { - const totalPages = Math.ceil(result.total / result.perPage); +function setResHeaders (req, res, result) { + const totalPages = Math.ceil(result.total / result.perPage) if (parseInt(result.page, 10) > 1) { - res.set("X-Prev-Page", parseInt(result.page, 10) - 1); + res.set('X-Prev-Page', parseInt(result.page, 10) - 1) } if (parseInt(result.page, 10) < totalPages) { - res.set("X-Next-Page", parseInt(result.page, 10) + 1); + res.set('X-Next-Page', parseInt(result.page, 10) + 1) } - res.set("X-Page", parseInt(result.page, 10)); - res.set("X-Per-Page", result.perPage); - res.set("X-Total", result.total); - res.set("X-Total-Pages", totalPages); + res.set('X-Page', parseInt(result.page, 10)) + res.set('X-Per-Page', result.perPage) + res.set('X-Total', result.total) + res.set('X-Total-Pages', totalPages) // set Link header if (totalPages > 0) { - let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink( - req, - totalPages - )}>; rel="last"`; + let link = `<${getPageLink(req, 1)}>; rel="first", <${getPageLink(req, totalPages)}>; rel="last"` if (parseInt(result.page, 10) > 1) { - link += `, <${getPageLink( - req, - parseInt(result.page, 10) - 1 - )}>; rel="prev"`; + link += `, <${getPageLink(req, parseInt(result.page, 10) - 1)}>; rel="prev"` } if (parseInt(result.page, 10) < totalPages) { - link += `, <${getPageLink( - req, - parseInt(result.page, 10) + 1 - )}>; rel="next"`; + link += `, <${getPageLink(req, parseInt(result.page, 10) + 1)}>; rel="next"` } - res.set("Link", link); + res.set('Link', link) } } @@ -130,18 +118,15 @@ function setResHeaders(req, res, result) { * Check if the user has admin role * @param {Object} authUser the user */ -function hasAdminRole(authUser) { +function hasAdminRole (authUser) { if (authUser && authUser.roles) { for (let i = 0; i < authUser.roles.length; i++) { - if ( - authUser.roles[i].toLowerCase() === - constants.UserRoles.Admin.toLowerCase() - ) { - return true; + if (authUser.roles[i].toLowerCase() === constants.UserRoles.Admin.toLowerCase()) { + return true } } } - return false; + return false } /** @@ -150,18 +135,16 @@ function hasAdminRole(authUser) { * @returns {Object} the new object with removed properties * @private */ -function _sanitizeObject(obj) { +function _sanitizeObject (obj) { try { - return JSON.parse( - JSON.stringify(obj, (name, value) => { - if (_.isArray(value) && value.length > 30) { - return `Array(${value.length})`; - } - return value; - }) - ); + return JSON.parse(JSON.stringify(obj, (name, value) => { + if (_.isArray(value) && value.length > 30) { + return `Array(${value.length})` + } + return value + })) } catch (e) { - return obj; + return obj } } @@ -170,8 +153,8 @@ function _sanitizeObject(obj) { * @param {Object} obj the object * @returns {String} the string value */ -function toString(obj) { - return util.inspect(_sanitizeObject(obj), { breakLength: Infinity }); +function toString (obj) { + return util.inspect(_sanitizeObject(obj), { breakLength: Infinity }) } /** @@ -180,30 +163,30 @@ function toString(obj) { * @param {Array} source the array in which to search for the term * @param {Array | String} term the term to search */ -function checkIfExists(source, term) { - let terms; +function checkIfExists (source, term) { + let terms if (!_.isArray(source)) { - throw new Error("Source argument should be an array"); + throw new Error('Source argument should be an array') } - source = source.map((s) => s.toLowerCase()); + source = source.map(s => s.toLowerCase()) if (_.isString(term)) { - terms = term.toLowerCase().split(" "); + terms = term.toLowerCase().split(' ') } else if (_.isArray(term)) { - terms = term.map((t) => t.toLowerCase()); + terms = term.map(t => t.toLowerCase()) } else { - throw new Error("Term argument should be either a string or an array"); + throw new Error('Term argument should be either a string or an array') } for (let i = 0; i < terms.length; i++) { if (source.includes(terms[i])) { - return true; + return true } } - return false; + return false } /** @@ -212,26 +195,19 @@ function checkIfExists(source, term) { * @param {String} id The id value * @returns {Promise} */ -async function getById(modelName, id) { +async function getById (modelName, id) { return new Promise((resolve, reject) => { - models[modelName] - .query("id") - .eq(id) - .exec((err, result) => { - if (err) { - return reject(err); - } - if (result.length > 0) { - return resolve(result[0]); - } else { - return reject( - new errors.NotFoundError( - `${modelName} with id: ${id} doesn't exist` - ) - ); - } - }); - }); + models[modelName].query('id').eq(id).exec((err, result) => { + if (err) { + return reject(err) + } + if (result.length > 0) { + return resolve(result[0]) + } else { + return reject(new errors.NotFoundError(`${modelName} with id: ${id} doesn't exist`)) + } + }) + }) } /** @@ -240,13 +216,13 @@ async function getById(modelName, id) { * @param {Array} ids The ids * @returns {Promise} the found entities */ -async function getByIds(modelName, ids) { - const entities = []; - const theIds = ids || []; +async function getByIds (modelName, ids) { + const entities = [] + const theIds = ids || [] for (const id of theIds) { - entities.push(await getById(modelName, id)); + entities.push(await getById(modelName, id)) } - return entities; + return entities } /** @@ -256,16 +232,11 @@ async function getByIds(modelName, ids) { * @param {String} value The attribute value to be validated * @returns {Promise} */ -async function validateDuplicate(modelName, name, value) { - const list = await scan(modelName); +async function validateDuplicate (modelName, name, value) { + const list = await scan(modelName) for (let i = 0; i < list.length; i++) { - if ( - list[i][name] && - String(list[i][name]).toLowerCase() === String(value).toLowerCase() - ) { - throw new errors.ConflictError( - `${modelName} with ${name}: ${value} already exist` - ); + if (list[i][name] && String(list[i][name]).toLowerCase() === String(value).toLowerCase()) { + throw new errors.ConflictError(`${modelName} with ${name}: ${value} already exist`) } } } @@ -276,17 +247,17 @@ async function validateDuplicate(modelName, name, value) { * @param {Object} data The create data object * @returns {Promise} */ -async function create(modelName, data) { +async function create (modelName, data) { return new Promise((resolve, reject) => { - const dbItem = new models[modelName](data); + const dbItem = new models[modelName](data) dbItem.save((err) => { if (err) { - return reject(err); + return reject(err) } else { - return resolve(dbItem); + return resolve(dbItem) } - }); - }); + }) + }) } /** @@ -295,19 +266,19 @@ async function create(modelName, data) { * @param {Object} data The updated data object * @returns {Promise} */ -async function update(dbItem, data) { +async function update (dbItem, data) { Object.keys(data).forEach((key) => { - dbItem[key] = data[key]; - }); + dbItem[key] = data[key] + }) return new Promise((resolve, reject) => { dbItem.save((err) => { if (err) { - return reject(err); + return reject(err) } else { - return resolve(dbItem); + return resolve(dbItem) } - }); - }); + }) + }) } /** @@ -316,16 +287,16 @@ async function update(dbItem, data) { * @param {Object} scanParams The scan parameters object * @returns {Promise} */ -async function scan(modelName, scanParams) { +async function scan (modelName, scanParams) { return new Promise((resolve, reject) => { models[modelName].scan(scanParams).exec((err, result) => { if (err) { - return reject(err); + return reject(err) } else { - return resolve(result.count === 0 ? [] : result); + return resolve(result.count === 0 ? [] : result) } - }); - }); + }) + }) } /** @@ -334,18 +305,15 @@ async function scan(modelName, scanParams) { * @param {Object} scanParams The scan parameters object * @returns {Array} */ -async function scanAll(modelName, scanParams) { - let results = await models[modelName].scan(scanParams).exec(); - let lastKey = results.lastKey; +async function scanAll (modelName, scanParams) { + let results = await models[modelName].scan(scanParams).exec() + let lastKey = results.lastKey while (!_.isUndefined(results.lastKey)) { - const newResult = await models[modelName] - .scan(scanParams) - .startAt(lastKey) - .exec(); - results = [...results, ...newResult]; - lastKey = newResult.lastKey; + const newResult = await models[modelName].scan(scanParams).startAt(lastKey).exec() + results = [...results, ...newResult] + lastKey = newResult.lastKey } - return results; + return results } /** @@ -354,16 +322,16 @@ async function scanAll(modelName, scanParams) { * @param {String} value the value to test * @returns {Boolean} the match result */ -function partialMatch(filter, value) { +function partialMatch (filter, value) { if (filter) { if (value) { - const filtered = xss(filter); - return _.toLower(value).includes(_.toLower(filtered)); + const filtered = xss(filter) + return _.toLower(value).includes(_.toLower(filtered)) } else { - return false; + return false } } else { - return true; + return true } } @@ -371,20 +339,18 @@ function partialMatch(filter, value) { * Perform validation on phases. * @param {Array} phases the phases data. */ -async function validatePhases(phases) { +async function validatePhases (phases) { if (!phases || phases.length === 0) { - return; + return } - const records = await scan("Phase"); - const map = new Map(); - _.each(records, (r) => { - map.set(r.id, r); - }); - const invalidPhases = _.filter(phases, (p) => !map.has(p.phaseId)); + const records = await scan('Phase') + const map = new Map() + _.each(records, r => { + map.set(r.id, r) + }) + const invalidPhases = _.filter(phases, p => !map.has(p.phaseId)) if (invalidPhases.length > 0) { - throw new errors.BadRequestError( - `The following phases are invalid: ${toString(invalidPhases)}` - ); + throw new errors.BadRequestError(`The following phases are invalid: ${toString(invalidPhases)}`) } } @@ -394,12 +360,12 @@ async function validatePhases(phases) { * @param {String} key the key name * @return {Promise} promise resolved to downloaded data */ -async function downloadFromFileStack(url) { - const res = await axios.get(url); +async function downloadFromFileStack (url) { + const res = await axios.get(url) return { data: res.data, - mimetype: _.get(res, `headers['content-type']`, "application/json"), - }; + mimetype: _.get(res, `headers['content-type']`, 'application/json') + } } /** @@ -408,12 +374,12 @@ async function downloadFromFileStack(url) { * @param {String} key the key name * @return {Promise} promise resolved to downloaded data */ -async function downloadFromS3(bucket, key) { - const file = await s3.getObject({ Bucket: bucket, Key: key }).promise(); +async function downloadFromS3 (bucket, key) { + const file = await s3.getObject({ Bucket: bucket, Key: key }).promise() return { data: file.Body, - mimetype: file.ContentType, - }; + mimetype: file.ContentType + } } /** @@ -422,19 +388,16 @@ async function downloadFromS3(bucket, key) { * @param {String} key the key name * @return {Promise} promise resolved to deleted data */ -async function deleteFromS3(bucket, key) { - return s3.deleteObject({ Bucket: bucket, Key: key }).promise(); +async function deleteFromS3 (bucket, key) { + return s3.deleteObject({ Bucket: bucket, Key: key }).promise() } /** * Get M2M token. * @returns {Promise} the M2M token */ -async function getM2MToken() { - return m2m.getMachineToken( - config.AUTH0_CLIENT_ID, - config.AUTH0_CLIENT_SECRET - ); +async function getM2MToken () { + return m2m.getMachineToken(config.AUTH0_CLIENT_ID, config.AUTH0_CLIENT_SECRET) } /** @@ -442,29 +405,24 @@ async function getM2MToken() { * @param {String} challengeId the challenge id * @returns {Promise} the challenge resources */ -async function getChallengeResources(challengeId) { - const token = await getM2MToken(); - const perPage = 100; - let page = 1; - let result = []; +async function getChallengeResources (challengeId) { + const token = await getM2MToken() + const perPage = 100 + let page = 1 + let result = [] while (true) { - const url = `${config.RESOURCES_API_URL}?challengeId=${challengeId}&perPage=${perPage}&page=${page}`; - const res = await axios.get(url, { - headers: { Authorization: `Bearer ${token}` }, - }); + const url = `${config.RESOURCES_API_URL}?challengeId=${challengeId}&perPage=${perPage}&page=${page}` + const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }) if (!res.data || res.data.length === 0) { - break; + break } - result = result.concat(res.data); - page += 1; - if ( - res.headers["x-total-pages"] && - page > Number(res.headers["x-total-pages"]) - ) { - break; + result = result.concat(res.data) + page += 1 + if (res.headers['x-total-pages'] && page > Number(res.headers['x-total-pages'])) { + break } } - return result; + return result } /** @@ -473,19 +431,17 @@ async function getChallengeResources(challengeId) { * @param {String} memberHandle the user's member handle * @param {String} roleId the resource role ID to assign */ -async function createResource(challengeId, memberHandle, roleId) { - const token = await getM2MToken(); +async function createResource (challengeId, memberHandle, roleId) { + const token = await getM2MToken() const userObj = { challengeId, memberHandle, - roleId, - }; - const url = `${config.RESOURCES_API_URL}`; - const res = await axios.post(url, userObj, { - headers: { Authorization: `Bearer ${token}` }, - }); - return res || false; + roleId + } + const url = `${config.RESOURCES_API_URL}` + const res = await axios.post(url, userObj, { headers: { Authorization: `Bearer ${token}` } }) + return res || false } /** @@ -496,94 +452,73 @@ async function createResource(challengeId, memberHandle, roleId) { * @param {String} token The token * @returns */ -async function createSelfServiceProject(name, description, type, token) { +async function createSelfServiceProject (name, description, type, token) { const projectObj = { name, description, - type, - }; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); - const url = `${config.PROJECTS_API_URL}`; - const res = await axios.post(url, projectObj, { - headers: { Authorization: `Bearer ${token}` }, - }); - return _.get(res, "data.id"); + type + } + const url = `${config.PROJECTS_API_URL}` + const res = await axios.post(url, projectObj, { headers: { Authorization: `Bearer ${token}` } }) + return _.get(res, 'data.id') } /** * Get project id by roundId * @param {String} roundId the round id */ -async function getProjectIdByRoundId(roundId) { - const url = `${config.CHALLENGE_MIGRATION_APP_URL}/getChallengeProjectId/${roundId}`; - const res = await axios.get(url); - return _.get(res, "data.projectId"); +async function getProjectIdByRoundId (roundId) { + const url = `${config.CHALLENGE_MIGRATION_APP_URL}/getChallengeProjectId/${roundId}` + const res = await axios.get(url) + return _.get(res, 'data.projectId') } /** * Get project payment * @param {String} projectId the project id */ -async function getProjectPayment(projectId) { - const token = await getM2MToken(); - const url = `${config.CUSTOMER_PAYMENTS_URL}`; +async function getProjectPayment (projectId) { + const token = await getM2MToken() + const url = `${config.CUSTOMER_PAYMENTS_URL}` const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` }, params: { referenceId: projectId, - reference: "project", - }, - }); - const [payment] = res.data; - return payment; + reference: 'project' + } + }) + const [payment] = res.data + return payment } /** * Charge payment * @param {String} paymentId the payment ID */ -async function capturePayment(paymentId) { - const token = await getM2MToken(); - const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/charge`; - logger.info(`Calling: ${url} to capture payment`); - const res = await axios.patch( - url, - {}, - { headers: { Authorization: `Bearer ${token}` } } - ); - logger.debug(`Payment API Response: ${JSON.stringify(res.data, null, 2)}`); - if (res.data.status !== "succeeded") { - throw new Error( - `Failed to charge payment. Current status: ${res.data.status}` - ); +async function capturePayment (paymentId) { + const token = await getM2MToken() + const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/charge` + logger.info(`Calling: ${url} to capture payment`) + const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }) + logger.debug(`Payment API Response: ${JSON.stringify(res.data, null, 2)}`) + if (res.data.status !== 'succeeded') { + throw new Error(`Failed to charge payment. Current status: ${res.data.status}`) } - return res.data; + return res.data } /** * Cancel payment * @param {String} paymentId the payment ID */ -async function cancelPayment(paymentId) { - const token = await getM2MToken(); - const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/cancel`; - const res = await axios.patch( - url, - {}, - { headers: { Authorization: `Bearer ${token}` } } - ); - if (res.data.status !== "canceled") { - throw new Error( - `Failed to cancel payment. Current status: ${res.data.status}` - ); +async function cancelPayment (paymentId) { + const token = await getM2MToken() + const url = `${config.CUSTOMER_PAYMENTS_URL}/${paymentId}/cancel` + const res = await axios.patch(url, {}, { headers: { Authorization: `Bearer ${token}` } }) + if (res.data.status !== 'canceled') { + throw new Error(`Failed to cancel payment. Current status: ${res.data.status}`) } - return res.data; + return res.data } /** @@ -592,41 +527,30 @@ async function cancelPayment(paymentId) { * @param {String} cancelReason the cancel reasonn * @param {Object} currentUser the current user */ -async function cancelProject(projectId, cancelReason, currentUser) { - let payment = await getProjectPayment(projectId); - const project = await ensureProjectExist(projectId, currentUser); - if (project.status === "cancelled") return; // already canceled +async function cancelProject (projectId, cancelReason, currentUser) { + let payment = await getProjectPayment(projectId) + const project = await ensureProjectExist(projectId, currentUser) + if (project.status === 'cancelled') return // already canceled try { - payment = await cancelPayment(payment.id); + payment = await cancelPayment(payment.id) } catch (e) { - logger.debug(`Failed to cancel payment with error: ${e.message}`); + logger.debug(`Failed to cancel payment with error: ${e.message}`) } - const token = await getM2MToken(); - const url = `${config.PROJECTS_API_URL}/${projectId}`; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); - await axios.patch( - url, - { - cancelReason, - status: "cancelled", - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status, - }, - }, - { headers: { Authorization: `Bearer ${token}` } } - ); + const token = await getM2MToken() + const url = `${config.PROJECTS_API_URL}/${projectId}` + await axios.patch(url, { + cancelReason, + status: 'cancelled', + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status + } + }, { headers: { Authorization: `Bearer ${token}` } }) } /** @@ -634,60 +558,41 @@ async function cancelProject(projectId, cancelReason, currentUser) { * @param {String} projectId the project id * @param {Object} currentUser the current user */ -async function activateProject(projectId, currentUser, name, description) { - let payment; - let project; +async function activateProject (projectId, currentUser, name, description) { + let payment + let project try { - payment = await getProjectPayment(projectId); - project = await ensureProjectExist(projectId, currentUser); - if (payment.status !== "succeeded") { - payment = await capturePayment(payment.id); + payment = await getProjectPayment(projectId) + project = await ensureProjectExist(projectId, currentUser) + if (payment.status !== 'succeeded') { + payment = await capturePayment(payment.id) } } catch (e) { - logger.debug(e); - logger.debug( - `Failed to charge payment ${payment.id} with error: ${e.message}` - ); - await cancelProject( - projectId, - `Failed to charge payment ${payment.id} with error: ${e.message}`, - currentUser - ); - throw new Error( - `Failed to charge payment ${payment.id} with error: ${e.message}` - ); + logger.debug(e) + logger.debug(`Failed to charge payment ${payment.id} with error: ${e.message}`) + await cancelProject(projectId, `Failed to charge payment ${payment.id} with error: ${e.message}`, currentUser) + throw new Error(`Failed to charge payment ${payment.id} with error: ${e.message}`) } - const token = await getM2MToken(); - const url = `${config.PROJECTS_API_URL}/${projectId}`; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); - const res = await axios.patch( - url, - { - name, - description, - status: "active", - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status, - }, - }, - { headers: { Authorization: `Bearer ${token}` } } - ); - - if (res.data && res.data.status === "reviewed") { + const token = await getM2MToken() + const url = `${config.PROJECTS_API_URL}/${projectId}` + const res = await axios.patch(url, { + name, + description, + status: 'active', + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status + } + }, { headers: { Authorization: `Bearer ${token}` } }) + + if (res.data && res.data.status === 'reviewed') { // auto activate if the project goes in reviewed state - await activateProject(projectId, currentUser, name, description); + await activateProject(projectId, currentUser, name, description) } } @@ -697,50 +602,33 @@ async function activateProject(projectId, currentUser, name, description) { * @param {*} workItemPlannedEndDate the planned end date of the work item * @param {*} currentUser the current user */ -async function updateSelfServiceProjectInfo( - projectId, - workItemPlannedEndDate, - currentUser -) { - const project = await ensureProjectExist(projectId, currentUser); - const payment = await getProjectPayment(projectId); - const token = await getM2MToken(); - const url = `${config.PROJECTS_API_URL}/${projectId}`; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); - const res = await axios.patch( - url, - { - details: { - ...project.details, - paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, - paymentId: payment.id, - paymentIntentId: payment.paymentIntentId, - paymentAmount: payment.amount, - paymentCurrency: payment.currency, - paymentStatus: payment.status, - workItemPlannedEndDate, - }, - }, - { headers: { Authorization: `Bearer ${token}` } } - ); +async function updateSelfServiceProjectInfo (projectId, workItemPlannedEndDate, currentUser) { + const project = await ensureProjectExist(projectId, currentUser) + const payment = await getProjectPayment(projectId) + const token = await getM2MToken() + const url = `${config.PROJECTS_API_URL}/${projectId}` + const res = await axios.patch(url, { + details: { + ...project.details, + paymentProvider: config.DEFAULT_PAYMENT_PROVIDER, + paymentId: payment.id, + paymentIntentId: payment.paymentIntentId, + paymentAmount: payment.amount, + paymentCurrency: payment.currency, + paymentStatus: payment.status, + workItemPlannedEndDate + } + }, { headers: { Authorization: `Bearer ${token}` } }) } /** * Get resource roles * @returns {Promise} the challenge resources */ -async function getResourceRoles() { - const token = await getM2MToken(); - const res = await axios.get(config.RESOURCE_ROLES_API_URL, { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.data || []; +async function getResourceRoles () { + const token = await getM2MToken() + const res = await axios.get(config.RESOURCE_ROLES_API_URL, { headers: { Authorization: `Bearer ${token}` } }) + return res.data || [] } /** @@ -748,21 +636,11 @@ async function getResourceRoles() { * @param {String} challengeId the challenge UUID * @param {String} userId the user ID */ -async function userHasFullAccess(challengeId, userId) { - const resourceRoles = await getResourceRoles(); - const rolesWithFullAccess = _.map( - _.filter(resourceRoles, (r) => r.fullWriteAccess), - "id" - ); - const challengeResources = await getChallengeResources(challengeId); - return ( - _.filter( - challengeResources, - (r) => - _.toString(r.memberId) === _.toString(userId) && - _.includes(rolesWithFullAccess, r.roleId) - ).length > 0 - ); +async function userHasFullAccess (challengeId, userId) { + const resourceRoles = await getResourceRoles() + const rolesWithFullAccess = _.map(_.filter(resourceRoles, r => r.fullWriteAccess), 'id') + const challengeResources = await getChallengeResources(challengeId) + return _.filter(challengeResources, r => _.toString(r.memberId) === _.toString(userId) && _.includes(rolesWithFullAccess, r.roleId)).length > 0 } /** @@ -770,11 +648,11 @@ async function userHasFullAccess(challengeId, userId) { * @param {String} userId the user id * @returns {Promise} the user groups */ -async function getUserGroups(userId) { - const token = await getM2MToken(); - let allGroups = []; +async function getUserGroups (userId) { + const token = await getM2MToken() + let allGroups = [] // get search is paginated, we need to get all pages' data - let page = 1; + let page = 1 while (true) { const result = await axios.get(config.GROUPS_API_URL, { headers: { Authorization: `Bearer ${token}` }, @@ -782,23 +660,20 @@ async function getUserGroups(userId) { page, perPage: 5000, memberId: userId, - membershipType: "user", - }, - }); - const groups = result.data || []; + membershipType: 'user' + } + }) + const groups = result.data || [] if (groups.length === 0) { - break; + break } - allGroups = allGroups.concat(groups); - page += 1; - if ( - result.headers["x-total-pages"] && - page > Number(result.headers["x-total-pages"]) - ) { - break; + allGroups = allGroups.concat(groups) + page += 1 + if (result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { + break } } - return allGroups; + return allGroups } /** @@ -806,19 +681,16 @@ async function getUserGroups(userId) { * @param {String} userId the user id * @returns {Promise} the user groups */ -async function getCompleteUserGroupTreeIds(userId) { - const token = await getM2MToken(); - const result = await axios.get( - `${config.GROUPS_API_URL}/memberGroups/${userId}`, - { - headers: { Authorization: `Bearer ${token}` }, - params: { - uuid: true, - }, +async function getCompleteUserGroupTreeIds (userId) { + const token = await getM2MToken() + const result = await axios.get(`${config.GROUPS_API_URL}/memberGroups/${userId}`, { + headers: { Authorization: `Bearer ${token}` }, + params: { + uuid: true } - ); + }) - return result.data || []; + return result.data || [] } /** @@ -826,16 +698,16 @@ async function getCompleteUserGroupTreeIds(userId) { * @param {String} groupId the group ID * @returns {Array} an array with the groups ID and the IDs of all subGroups */ -async function expandWithSubGroups(groupId) { - const token = await getM2MToken(); +async function expandWithSubGroups (groupId) { + const token = await getM2MToken() const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { headers: { Authorization: `Bearer ${token}` }, params: { - includeSubGroups: true, - }, - }); - const groups = result.data || {}; - return [groupId, ..._.map(_.get(groups, "subGroups", []), "id")]; + includeSubGroups: true + } + }) + const groups = result.data || {} + return [groupId, ..._.map(_.get(groups, 'subGroups', []), 'id')] } /** @@ -843,26 +715,26 @@ async function expandWithSubGroups(groupId) { * @param {String} groupId the group ID * @returns {Array} an array with the group ID and the IDs of all parent groups up the chain */ -async function expandWithParentGroups(groupId) { - const token = await getM2MToken(); +async function expandWithParentGroups (groupId) { + const token = await getM2MToken() const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { headers: { Authorization: `Bearer ${token}` }, params: { includeParentGroup: true, - oneLevel: false, - }, - }); + oneLevel: false + } + }) - const ids = []; + const ids = [] const extractIds = (group) => { - ids.push(group.id); - _.each(_.get(group, "parentGroups", []), (parent) => { - extractIds(parent); - }); - }; - - extractIds(result.data || {}); - return ids; + ids.push(group.id) + _.each(_.get(group, 'parentGroups', []), (parent) => { + extractIds(parent) + }) + } + + extractIds(result.data || {}) + return ids } /** @@ -870,17 +742,15 @@ async function expandWithParentGroups(groupId) { * @param {Array} arr the array to check * @param {String} name the array name */ -function ensureNoDuplicateOrNullElements(arr, name) { - const a = arr || []; +function ensureNoDuplicateOrNullElements (arr, name) { + const a = arr || [] for (let i = 0; i < a.length; i += 1) { if (_.isNil(a[i])) { - throw new errors.BadRequestError(`There is null element for ${name}.`); + throw new errors.BadRequestError(`There is null element for ${name}.`) } for (let j = i + 1; j < a.length; j += 1) { if (a[i] === a[j]) { - throw new errors.BadRequestError( - `There are duplicate elements (${a[i]}) for ${name}.` - ); + throw new errors.BadRequestError(`There are duplicate elements (${a[i]}) for ${name}.`) } } } @@ -890,24 +760,16 @@ function ensureNoDuplicateOrNullElements(arr, name) { * Get Bus API Client * @return {Object} Bus API Client Instance */ -function getBusApiClient() { +function getBusApiClient () { // if there is no bus API client instance, then create a new instance if (!busApiClient) { - busApiClient = busApi( - _.pick(config, [ - "AUTH0_URL", - "AUTH0_AUDIENCE", - "TOKEN_CACHE_TIME", - "AUTH0_CLIENT_ID", - "AUTH0_CLIENT_SECRET", - "BUSAPI_URL", - "KAFKA_ERROR_TOPIC", - "AUTH0_PROXY_SERVER_URL", - ]) - ); + busApiClient = busApi(_.pick(config, + ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', + 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET', 'BUSAPI_URL', + 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) } - return busApiClient; + return busApiClient } /** @@ -916,48 +778,48 @@ function getBusApiClient() { * @param {Object} payload the event payload * @param {Object} options the extra options to the message */ -async function postBusEvent(topic, payload, options = {}) { - const client = getBusApiClient(); +async function postBusEvent (topic, payload, options = {}) { + const client = getBusApiClient() const message = { topic, originator: constants.EVENT_ORIGINATOR, timestamp: new Date().toISOString(), - "mime-type": constants.EVENT_MIME_TYPE, - payload, - }; + 'mime-type': constants.EVENT_MIME_TYPE, + payload + } if (options.key) { - message.key = options.key; + message.key = options.key } - await client.postEvent(message); + await client.postEvent(message) } /** * Get ES Client * @return {Object} Elasticsearch Client Instance */ -function getESClient() { +function getESClient () { if (esClient) { - return esClient; + return esClient } - const esHost = config.get("ES.HOST"); + const esHost = config.get('ES.HOST') // AWS ES configuration is different from other providers if (/.*amazonaws.*/.test(esHost)) { esClient = elasticsearch.Client({ - apiVersion: config.get("ES.API_VERSION"), + apiVersion: config.get('ES.API_VERSION'), hosts: esHost, - connectionClass: require("http-aws-es"), // eslint-disable-line global-require + connectionClass: require('http-aws-es'), // eslint-disable-line global-require amazonES: { - region: config.get("AMAZON.AWS_REGION"), - credentials: new AWS.EnvironmentCredentials("AWS"), - }, - }); + region: config.get('AMAZON.AWS_REGION'), + credentials: new AWS.EnvironmentCredentials('AWS') + } + }) } else { esClient = new elasticsearch.Client({ - apiVersion: config.get("ES.API_VERSION"), - hosts: esHost, - }); + apiVersion: config.get('ES.API_VERSION'), + hosts: esHost + }) } - return esClient; + return esClient } /** @@ -965,51 +827,27 @@ function getESClient() { * @param {String} projectId the project id * @param {String} currentUser the user */ -async function ensureProjectExist(projectId, currentUser) { - let token = await getM2MToken(); - const url = `${config.PROJECTS_API_URL}/${projectId}`; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); +async function ensureProjectExist (projectId, currentUser) { + let token = await getM2MToken() + const url = `${config.PROJECTS_API_URL}/${projectId}` try { - const res = await axios.get(url, { - headers: { Authorization: `Bearer ${token}` }, - }); + const res = await axios.get(url, { headers: { Authorization: `Bearer ${token}` } }) if (currentUser.isMachine || hasAdminRole(currentUser)) { - return res.data; + return res.data } - if ( - _.get(res, "data.type") === "self-service" && - _.includes( - config.SELF_SERVICE_WHITELIST_HANDLES, - currentUser.handle.toLowerCase() - ) - ) { - return res.data; + if (_.get(res, 'data.type') === 'self-service' && _.includes(config.SELF_SERVICE_WHITELIST_HANDLES, currentUser.handle.toLowerCase())) { + return res.data } - if ( - !_.find( - _.get(res, "data.members", []), - (m) => _.toString(m.userId) === _.toString(currentUser.userId) - ) - ) { - throw new errors.ForbiddenError( - `You don't have access to project with ID: ${projectId}` - ); + if (!_.find(_.get(res, 'data.members', []), m => _.toString(m.userId) === _.toString(currentUser.userId))) { + throw new errors.ForbiddenError(`You don't have access to project with ID: ${projectId}`) } - return res.data; + return res.data } catch (err) { - if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError( - `Project with id: ${projectId} doesn't exist` - ); + if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`) } else { // re-throw other error - throw err; + throw err } } } @@ -1018,12 +856,12 @@ async function ensureProjectExist(projectId, currentUser) { * Calculates challenge end date based on its phases * @param {any} challenge */ -function calculateChallengeEndDate(challenge, data) { +function calculateChallengeEndDate (challenge, data) { if (!data) { - data = challenge; + data = challenge } - let lastPhase = data.phases[data.phases.length - 1]; - return lastPhase.actualEndDate || lastPhase.scheduledEndDate; + let lastPhase = data.phases[data.phases.length - 1] + return lastPhase.actualEndDate || lastPhase.scheduledEndDate // let phase = data.phases[data.phases.length - 1] // if (!phase || (!data.startDate && !challenge.startDate)) { // return data.startDate || challenge.startDate @@ -1045,46 +883,36 @@ function calculateChallengeEndDate(challenge, data) { * @param {Number} memberId the member id * @returns {Promise} an array of challenge ids represents challenges that given member has access to. */ -async function listChallengesByMember(memberId) { - const token = await getM2MToken(); - let allIds = []; +async function listChallengesByMember (memberId) { + const token = await getM2MToken() + let allIds = [] // get search is paginated, we need to get all pages' data - let page = 1; + let page = 1 while (true) { - let result = {}; + let result = {} try { - result = await axios.get( - `${config.RESOURCES_API_URL}/${memberId}/challenges`, - { - headers: { Authorization: `Bearer ${token}` }, - params: { - page, - perPage: 10000, - }, + result = await axios.get(`${config.RESOURCES_API_URL}/${memberId}/challenges`, { + headers: { Authorization: `Bearer ${token}` }, + params: { + page, + perPage: 10000 } - ); + }) } catch (e) { // only log the error but don't throw it, so the following logic can still be executed. - logger.debug( - `Failed to get challenges that accessible to the memberId ${memberId}`, - e - ); + logger.debug(`Failed to get challenges that accessible to the memberId ${memberId}`, e) } - const ids = result.data || []; + const ids = result.data || [] if (ids.length === 0) { - break; + break } - allIds = allIds.concat(ids); - page += 1; - if ( - result.headers && - result.headers["x-total-pages"] && - page > Number(result.headers["x-total-pages"]) - ) { - break; + allIds = allIds.concat(ids) + page += 1 + if (result.headers && result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { + break } } - return allIds; + return allIds } /** @@ -1093,12 +921,9 @@ async function listChallengesByMember(memberId) { * @param {String} method method to be tested * @returns {String} method valid method */ -async function validateESRefreshMethod(method) { - Joi.attempt( - method, - Joi.string().label("ES_REFRESH").valid(["true", "false", "wait_for"]) - ); - return method; +async function validateESRefreshMethod (method) { + Joi.attempt(method, Joi.string().label('ES_REFRESH').valid(['true', 'false', 'wait_for'])) + return method } /** @@ -1107,29 +932,18 @@ async function validateESRefreshMethod(method) { * @param {Number} projectId The id of the project for which to get the default terms of use * @returns {Promise>} An array containing the ids of the default project terms of use */ -async function getProjectDefaultTerms(projectId) { - const token = await getM2MToken(); - const projectUrl = `${config.PROJECTS_API_URL}/${projectId}`; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); +async function getProjectDefaultTerms (projectId) { + const token = await getM2MToken() + const projectUrl = `${config.PROJECTS_API_URL}/${projectId}` try { - const res = await axios.get(projectUrl, { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.data.terms || []; + const res = await axios.get(projectUrl, { headers: { Authorization: `Bearer ${token}` } }) + return res.data.terms || [] } catch (err) { - if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError( - `Project with id: ${projectId} doesn't exist` - ); + if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError(`Project with id: ${projectId} doesn't exist`) } else { // re-throw other error - throw err; + throw err } } } @@ -1140,40 +954,29 @@ async function getProjectDefaultTerms(projectId) { * @param {Number} projectId The id of the project for which to get the default terms of use * @returns {Promise} The billing account ID */ -async function getProjectBillingInformation(projectId) { - const token = await getM2MToken(); - const projectUrl = `${config.PROJECTS_API_URL}/${projectId}/billingAccount`; - axiosRetry(axios, { - retries: `${config.AXIOS_RETRY.RETRIES}`, // number of retries - retryCondition: (e) => { - return axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429; - }, - retryDelay: axiosRetry.exponentialDelay - }); +async function getProjectBillingInformation (projectId) { + const token = await getM2MToken() + const projectUrl = `${config.PROJECTS_API_URL}/${projectId}/billingAccount` try { - const res = await axios.get(projectUrl, { - headers: { Authorization: `Bearer ${token}` }, - }); - let markup = _.get(res, "data.markup", null) - ? _.toNumber(_.get(res, "data.markup", null)) - : null; + const res = await axios.get(projectUrl, { headers: { Authorization: `Bearer ${token}` } }) + let markup = _.get(res, 'data.markup', null) ? _.toNumber(_.get(res, 'data.markup', null)) : null if (markup && markup > 0) { // TODO - Hack to change int returned from api to decimal - markup = markup / 100; + markup = markup / 100 } return { - billingAccountId: _.get(res, "data.tcBillingAccountId", null), - markup, - }; + billingAccountId: _.get(res, 'data.tcBillingAccountId', null), + markup + } } catch (err) { - if (_.get(err, "response.status") === HttpStatus.NOT_FOUND) { + if (_.get(err, 'response.status') === HttpStatus.NOT_FOUND) { return { billingAccountId: null, - markup: null, - }; + markup: null + } } else { // re-throw other error - throw err; + throw err } } } @@ -1184,29 +987,25 @@ async function getProjectBillingInformation(projectId) { * * @param {Array} terms The array of terms {id, roleId} to retrieve from terms API */ -async function validateChallengeTerms(terms = []) { - const listOfTerms = []; - const token = await getM2MToken(); +async function validateChallengeTerms (terms = []) { + const listOfTerms = [] + const token = await getM2MToken() for (let term of terms) { // Get the terms details from the API try { - await axios.get(`${config.TERMS_API_URL}/${term.id}`, { - headers: { Authorization: `Bearer ${token}` }, - }); - listOfTerms.push(term); + await axios.get(`${config.TERMS_API_URL}/${term.id}`, { headers: { Authorization: `Bearer ${token}` } }) + listOfTerms.push(term) } catch (e) { - if (_.get(e, "response.status") === HttpStatus.NOT_FOUND) { - throw new errors.BadRequestError( - `Terms of use identified by the id ${term.id} does not exist` - ); + if (_.get(e, 'response.status') === HttpStatus.NOT_FOUND) { + throw new errors.BadRequestError(`Terms of use identified by the id ${term.id} does not exist`) } else { // re-throw other error - throw e; + throw e } } } - return listOfTerms; + return listOfTerms } /** @@ -1215,42 +1014,28 @@ async function validateChallengeTerms(terms = []) { * @param {Array} challenges the challenges to filter * @returns {Array} the challenges that can be accessed by current user */ -async function _filterChallengesByGroupsAccess(currentUser, challenges) { - const res = []; - const needToCheckForGroupAccess = !currentUser - ? true - : !currentUser.isMachine && !hasAdminRole(currentUser); - if (!needToCheckForGroupAccess) return challenges; +async function _filterChallengesByGroupsAccess (currentUser, challenges) { + const res = [] + const needToCheckForGroupAccess = !currentUser ? true : !currentUser.isMachine && !hasAdminRole(currentUser) + if (!needToCheckForGroupAccess) return challenges - let userGroups; + let userGroups for (const challenge of challenges) { - challenge.groups = _.filter( - challenge.groups, - (g) => !_.includes(["null", "undefined"], _.toString(g).toLowerCase()) - ); - if ( - !challenge.groups || - _.get(challenge, "groups.length", 0) === 0 || - !needToCheckForGroupAccess - ) { - res.push(challenge); + challenge.groups = _.filter(challenge.groups, g => !_.includes(['null', 'undefined'], _.toString(g).toLowerCase())) + if (!challenge.groups || _.get(challenge, 'groups.length', 0) === 0 || !needToCheckForGroupAccess) { + res.push(challenge) } else if (currentUser) { if (_.isNil(userGroups)) { - userGroups = await getCompleteUserGroupTreeIds(currentUser.userId); + userGroups = await getCompleteUserGroupTreeIds(currentUser.userId) } // get user groups if not yet - if ( - _.find( - challenge.groups, - (group) => !!_.find(userGroups, (ug) => ug === group) - ) - ) { - res.push(challenge); + if (_.find(challenge.groups, (group) => !!_.find(userGroups, (ug) => ug === group))) { + res.push(challenge) } } } - return res; + return res } /** @@ -1258,14 +1043,10 @@ async function _filterChallengesByGroupsAccess(currentUser, challenges) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function ensureAccessibleByGroupsAccess(currentUser, challenge) { - const filtered = await _filterChallengesByGroupsAccess(currentUser, [ - challenge, - ]); +async function ensureAccessibleByGroupsAccess (currentUser, challenge) { + const filtered = await _filterChallengesByGroupsAccess(currentUser, [challenge]) if (filtered.length === 0) { - throw new errors.ForbiddenError( - "helper ensureAcessibilityToModifiedGroups :: You don't have access to this group!" - ); + throw new errors.ForbiddenError("helper ensureAcessibilityToModifiedGroups :: You don't have access to this group!") } } @@ -1275,30 +1056,19 @@ async function ensureAccessibleByGroupsAccess(currentUser, challenge) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function _ensureAccessibleForTaskChallenge(currentUser, challenge) { - let challengeResourceIds; +async function _ensureAccessibleForTaskChallenge (currentUser, challenge) { + let challengeResourceIds // Check if challenge is task and apply security rules - if ( - _.get(challenge, "task.isTask", false) && - _.get(challenge, "task.isAssigned", false) - ) { + if (_.get(challenge, 'task.isTask', false) && _.get(challenge, 'task.isAssigned', false)) { if (currentUser) { if (!currentUser.isMachine) { - const challengeResources = await getChallengeResources(challenge.id); - challengeResourceIds = _.map(challengeResources, (r) => - _.toString(r.memberId) - ); + const challengeResources = await getChallengeResources(challenge.id) + challengeResourceIds = _.map(challengeResources, r => _.toString(r.memberId)) } } - const canAccesChallenge = _.isUndefined(currentUser) - ? false - : currentUser.isMachine || - hasAdminRole(currentUser) || - _.includes(challengeResourceIds || [], _.toString(currentUser.userId)); + const canAccesChallenge = _.isUndefined(currentUser) ? false : currentUser.isMachine || hasAdminRole(currentUser) || _.includes((challengeResourceIds || []), _.toString(currentUser.userId)) if (!canAccesChallenge) { - throw new errors.ForbiddenError( - `You don't have access to view this challenge` - ); + throw new errors.ForbiddenError(`You don't have access to view this challenge`) } } } @@ -1309,11 +1079,11 @@ async function _ensureAccessibleForTaskChallenge(currentUser, challenge) { * @param {Object} currentUser the user who perform operation * @param {Object} challenge the challenge to check */ -async function ensureUserCanViewChallenge(currentUser, challenge) { +async function ensureUserCanViewChallenge (currentUser, challenge) { // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge); + await ensureAccessibleByGroupsAccess(currentUser, challenge) // check if user can access a challenge that is a task - await _ensureAccessibleForTaskChallenge(currentUser, challenge); + await _ensureAccessibleForTaskChallenge(currentUser, challenge) } /** @@ -1323,60 +1093,50 @@ async function ensureUserCanViewChallenge(currentUser, challenge) { * @param {Object} challenge the challenge to check * @returns {undefined} */ -async function ensureUserCanModifyChallenge(currentUser, challenge) { +async function ensureUserCanModifyChallenge (currentUser, challenge) { // check groups authorization - await ensureAccessibleByGroupsAccess(currentUser, challenge); + await ensureAccessibleByGroupsAccess(currentUser, challenge) // check full access - const isUserHasFullAccess = await userHasFullAccess( - challenge.id, - currentUser.userId - ); - if ( - !currentUser.isMachine && - !hasAdminRole(currentUser) && - challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && - !isUserHasFullAccess - ) { - throw new errors.ForbiddenError( - `Only M2M, admin, challenge's copilot or users with full access can perform modification.` - ); + const isUserHasFullAccess = await userHasFullAccess(challenge.id, currentUser.userId) + if (!currentUser.isMachine && !hasAdminRole(currentUser) && challenge.createdBy.toLowerCase() !== currentUser.handle.toLowerCase() && !isUserHasFullAccess) { + throw new errors.ForbiddenError(`Only M2M, admin, challenge's copilot or users with full access can perform modification.`) } } /** - * Calculate the sum of prizes. - * - * @param {Array} prizes the list of prize - * @returns {Number} the result prize - */ -function sumOfPrizes(prizes) { - let sum = 0; + * Calculate the sum of prizes. + * + * @param {Array} prizes the list of prize + * @returns {Number} the result prize + */ +function sumOfPrizes (prizes) { + let sum = 0 if (!prizes.length) { - return sum; + return sum } for (const prize of prizes) { - sum += prize.value; + sum += prize.value } - return sum; + return sum } /** - * Get group by id - * @param {String} groupId the group id - * @returns {Promise} the group - */ -async function getGroupById(groupId) { - const token = await getM2MToken(); + * Get group by id + * @param {String} groupId the group id + * @returns {Promise} the group + */ +async function getGroupById (groupId) { + const token = await getM2MToken() try { const result = await axios.get(`${config.GROUPS_API_URL}/${groupId}`, { - headers: { Authorization: `Bearer ${token}` }, - }); - return result.data; + headers: { Authorization: `Bearer ${token}` } + }) + return result.data } catch (err) { if (err.response.status === HttpStatus.NOT_FOUND) { - return; + return } - throw err; + throw err } } @@ -1385,36 +1145,30 @@ async function getGroupById(groupId) { * @param {String} challengeId the challenge id * @returns {Array} the submission */ -async function getChallengeSubmissions(challengeId) { - const token = await getM2MToken(); - let allSubmissions = []; +async function getChallengeSubmissions (challengeId) { + const token = await getM2MToken() + let allSubmissions = [] // get search is paginated, we need to get all pages' data - let page = 1; + let page = 1 while (true) { - const result = await axios.get( - `${config.SUBMISSIONS_API_URL}?challengeId=${challengeId}`, - { - headers: { Authorization: `Bearer ${token}` }, - params: { - page, - perPage: 100, - }, + const result = await axios.get(`${config.SUBMISSIONS_API_URL}?challengeId=${challengeId}`, { + headers: { Authorization: `Bearer ${token}` }, + params: { + page, + perPage: 100 } - ); - const ids = result.data || []; + }) + const ids = result.data || [] if (ids.length === 0) { - break; + break } - allSubmissions = allSubmissions.concat(ids); - page += 1; - if ( - result.headers["x-total-pages"] && - page > Number(result.headers["x-total-pages"]) - ) { - break; + allSubmissions = allSubmissions.concat(ids) + page += 1 + if (result.headers['x-total-pages'] && page > Number(result.headers['x-total-pages'])) { + break } } - return allSubmissions; + return allSubmissions } /** @@ -1422,13 +1176,13 @@ async function getChallengeSubmissions(challengeId) { * @param {String} userId the user ID * @returns {Object} */ -async function getMemberById(userId) { - const token = await getM2MToken(); +async function getMemberById (userId) { + const token = await getM2MToken() const res = await axios.get(`${config.MEMBERS_API_URL}?userId=${userId}`, { - headers: { Authorization: `Bearer ${token}` }, - }); - if (res.data.length > 0) return res.data[0]; - return {}; + headers: { Authorization: `Bearer ${token}` } + }) + if (res.data.length > 0) return res.data[0] + return {} } /** @@ -1436,12 +1190,12 @@ async function getMemberById(userId) { * @param {String} handle the user handle * @returns {Object} */ -async function getMemberByHandle(handle) { - const token = await getM2MToken(); +async function getMemberByHandle (handle) { + const token = await getM2MToken() const res = await axios.get(`${config.MEMBERS_API_URL}/${handle}`, { - headers: { Authorization: `Bearer ${token}` }, - }); - return res.data || {}; + headers: { Authorization: `Bearer ${token}` } + }) + return res.data || {} } /** @@ -1450,12 +1204,12 @@ async function getMemberByHandle(handle) { * @param {Array} recipients the array of recipients in { userId || email || handle } format * @param {Object} data the data */ -async function sendSelfServiceNotification(type, recipients, data) { +async function sendSelfServiceNotification (type, recipients, data) { try { await postBusEvent(constants.Topics.Notifications, { notifications: [ { - serviceId: "email", + serviceId: 'email', type, details: { from: config.EMAIL_FROM, @@ -1463,18 +1217,16 @@ async function sendSelfServiceNotification(type, recipients, data) { cc: [...constants.SelfServiceNotificationSettings[type].cc], data: { ...data, - supportUrl: `${config.SELF_SERVICE_APP_URL}/support`, + supportUrl: `${config.SELF_SERVICE_APP_URL}/support` }, - sendgridTemplateId: - constants.SelfServiceNotificationSettings[type] - .sendgridTemplateId, - version: "v3", - }, - }, - ], - }); + sendgridTemplateId: constants.SelfServiceNotificationSettings[type].sendgridTemplateId, + version: 'v3' + } + } + ] + }) } catch (e) { - logger.debug(`Failed to post notification ${type}: ${e.message}`); + logger.debug(`Failed to post notification ${type}: ${e.message}`) } } @@ -1482,35 +1234,31 @@ async function sendSelfServiceNotification(type, recipients, data) { * Submit a request to zendesk * @param {Object} request the request */ -async function submitZendeskRequest(request) { +async function submitZendeskRequest (request) { try { - const res = await axios.post( - `${config.ZENDESK_API_URL}/api/v2/requests`, - { - request: { - ...request, - }, - }, - { - auth: { - username: `${request.requester.email}/token`, - password: config.ZENDESK_API_TOKEN, - }, + const res = await axios.post(`${config.ZENDESK_API_URL}/api/v2/requests`, { + request: { + ...request + } + }, { + auth: { + username: `${request.requester.email}/token`, + password: config.ZENDESK_API_TOKEN } - ); - return res.data || {}; + }) + return res.data || {} } catch (e) { - logger.debug(`Failed to submit request: ${e.message}`); - throw e; + logger.debug(`Failed to submit request: ${e.message}`) + throw e } } -function getFromInternalCache(key) { - return internalCache.get(key); +function getFromInternalCache (key) { + return internalCache.get(key) } -function setToInternalCache(key, value) { - internalCache.set(key, value); +function setToInternalCache (key, value) { + internalCache.set(key, value) } module.exports = { @@ -1569,5 +1317,5 @@ module.exports = { submitZendeskRequest, updateSelfServiceProjectInfo, getFromInternalCache, - setToInternalCache, -}; + setToInternalCache +} From 192f3bcddb041b665c9f56dd4c9c71c05729bdc1 Mon Sep 17 00:00:00 2001 From: Himani Raghav Date: Fri, 13 Jan 2023 19:54:26 +0530 Subject: [PATCH 3/4] requested changes added --- config/default.js | 2 + package-lock.json | 149 +++++++++++++++++++++++++------------------ package.json | 1 + src/common/helper.js | 17 +++++ 4 files changed, 108 insertions(+), 61 deletions(-) diff --git a/config/default.js b/config/default.js index babe7a5e..70c0d29f 100644 --- a/config/default.js +++ b/config/default.js @@ -91,6 +91,8 @@ module.exports = { NEW_SELF_SERVICE_PROJECT_TYPE: process.env.NEW_SELF_SERVICE_PROJECT_TYPE || 'self-service', + AXIOS_RETRIES: process.env.AXIOS_RETRIES || 3, + SENDGRID_TEMPLATES: { WORK_REQUEST_SUBMITTED: process.env.WORK_REQUEST_SUBMITTED || '', WORK_REQUEST_STARTED: process.env.WORK_REQUEST_STARTED || '', diff --git a/package-lock.json b/package-lock.json index a70c4fae..8c0bc8cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,6 +112,14 @@ "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==", "dev": true }, + "@babel/runtime": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, "@babel/template": { "version": "7.16.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz", @@ -548,6 +556,15 @@ "follow-redirects": "1.5.10" } }, + "axios-retry": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.3.1.tgz", + "integrity": "sha512-RohAUQTDxBSWLFEnoIG/6bvmy8l3TfpkclgStjl5MDCMBDgapAWCmr1r/9harQfWC8bzLC8job6UcL1A1Yc+/Q==", + "requires": { + "@babel/runtime": "^7.15.4", + "is-retry-allowed": "^2.2.0" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -866,7 +883,7 @@ "semver": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.0.1.tgz", - "integrity": "sha1-n7P0AE+QDYPEeWj+QvdYPgWDLMk=" + "integrity": "sha512-Ne6/HdGZvvpXBdjW3o8J0pvxC2jnmVNBK7MKkMgsOBfrsIdTXfA5x+H9DUbQ2xzyvnLv0A0v9x8R4B40xNZIRQ==" } } }, @@ -1507,7 +1524,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -1747,7 +1764,7 @@ "path-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==", "dev": true, "requires": { "pify": "^2.0.0" @@ -1756,13 +1773,13 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==", "dev": true, "requires": { "load-json-file": "^2.0.0", @@ -1773,7 +1790,7 @@ "read-pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==", "dev": true, "requires": { "find-up": "^2.0.0", @@ -2607,7 +2624,7 @@ "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", "dev": true, "requires": { "ansi-regex": "^3.0.0" @@ -2746,6 +2763,11 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-retry-allowed": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", + "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==" + }, "is-shared-array-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", @@ -3170,7 +3192,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true } } @@ -3829,18 +3851,18 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true }, "path-parse": { @@ -3852,7 +3874,7 @@ "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "path-type": { "version": "3.0.0", @@ -3866,7 +3888,7 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true } } @@ -3880,7 +3902,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "pify": { "version": "4.0.1", @@ -3891,7 +3913,7 @@ "pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", "dev": true, "requires": { "find-up": "^2.0.0", @@ -3946,7 +3968,7 @@ "pkg-config": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", - "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "integrity": "sha512-ft/WI9YK6FuTuw4Ql+QUaNXtm/ASQNqDUUsZEgFZKyFpW6amyP8Gx01xrRs8KdiNbbqXfYxkOXplpq1euWbOjw==", "dev": true, "requires": { "debug-log": "^1.0.0", @@ -3972,12 +3994,12 @@ "precond": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==" }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", "dev": true }, "process-nextick-args": { @@ -4019,7 +4041,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" }, "psl": { "version": "1.8.0", @@ -4029,12 +4051,12 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" }, "qs": { "version": "6.10.3", @@ -4047,7 +4069,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, "queue-microtask": { "version": "1.2.3", @@ -4076,7 +4098,7 @@ "semver": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.1.0.tgz", - "integrity": "sha1-hfLPhVBGXE3wAM99hvawVBBqueU=" + "integrity": "sha512-sfKXKhcz5XVyfUZa2V4RbjK0xjOJCMLNF9H4p4v0UCo9wNHM/lH9RDuyDbGEtxWLMDlPBc8xI7AbbVLKXty+rQ==" } } }, @@ -4105,7 +4127,7 @@ "read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, "requires": { "load-json-file": "^4.0.0", @@ -4147,11 +4169,16 @@ "reconnect-core": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/reconnect-core/-/reconnect-core-1.3.0.tgz", - "integrity": "sha1-+65SkZp4d9hE4yRtAaLyZwHIM8g=", + "integrity": "sha512-+gLKwmyRf2tjl6bLR03DoeWELzyN6LW9Xgr3vh7NXHHwPi0JC0N2TwPyf90oUEBkCRcD+bgQ+s3HORoG3nwHDg==", "requires": { "backoff": "~2.5.0" } }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, "regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -4172,7 +4199,7 @@ "release-zalgo": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", - "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==", "dev": true, "requires": { "es6-error": "^4.0.1" @@ -4215,7 +4242,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-main-filename": { @@ -4227,7 +4254,7 @@ "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "integrity": "sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w==", "dev": true, "requires": { "caller-path": "^0.1.0", @@ -4237,7 +4264,7 @@ "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "integrity": "sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg==", "dev": true } } @@ -4262,7 +4289,7 @@ "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", "dev": true, "requires": { "onetime": "^2.0.0", @@ -4272,7 +4299,7 @@ "rimraf": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", "optional": true, "requires": { "glob": "^6.0.1" @@ -4326,7 +4353,7 @@ "sax": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, "semver": { "version": "5.7.1", @@ -4389,7 +4416,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, "setprototypeof": { @@ -4400,7 +4427,7 @@ "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, "requires": { "shebang-regex": "^1.0.0" @@ -4409,7 +4436,7 @@ "shebang-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true }, "side-channel": { @@ -4431,7 +4458,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "^0.3.1" } @@ -4525,7 +4552,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "sshpk": { "version": "1.17.0", @@ -4546,7 +4573,7 @@ "stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==" }, "standard": { "version": "12.0.1", @@ -4654,7 +4681,7 @@ "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "requires": { "ansi-regex": "^2.0.0" } @@ -4662,13 +4689,13 @@ "strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true }, "superagent": { @@ -4691,7 +4718,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==" }, "supports-preserve-symlinks-flag": { "version": "1.0.0", @@ -4715,7 +4742,7 @@ "symbol-observable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", "dev": true }, "table": { @@ -4813,13 +4840,13 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, "tmp": { @@ -4834,7 +4861,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "toidentifier": { @@ -4901,7 +4928,7 @@ "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "requires": { "safe-buffer": "^5.0.1" } @@ -4909,12 +4936,12 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", "dev": true, "requires": { "prelude-ls": "~1.1.2" @@ -4950,13 +4977,13 @@ "uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", "dev": true }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "uri-js": { "version": "4.4.1", @@ -4976,7 +5003,7 @@ "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", "requires": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -4985,12 +5012,12 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" }, "uuid": { "version": "3.4.0", @@ -5010,12 +5037,12 @@ "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -5047,7 +5074,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==", "dev": true }, "wide-align": { @@ -5167,12 +5194,12 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "integrity": "sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA==", "dev": true, "requires": { "mkdirp": "^0.5.1" @@ -5201,7 +5228,7 @@ "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==" }, "xss": { "version": "1.0.11", @@ -5227,7 +5254,7 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, "yamljs": { "version": "0.3.0", diff --git a/package.json b/package.json index 3d2b8733..533a2cfe 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "dependencies": { "aws-sdk": "^2.1145.0", "axios": "^0.19.0", + "axios-retry": "^3.3.1", "bluebird": "^3.5.1", "body-parser": "^1.15.1", "config": "^3.0.1", diff --git a/src/common/helper.js b/src/common/helper.js index 0db4a781..eb8dad02 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -13,6 +13,7 @@ const config = require('config') const m2mAuth = require('tc-core-library-js').auth.m2m const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME'])) const axios = require('axios') +const axiosRetry = require('axios-retry') const busApi = require('topcoder-bus-api-wrapper') const elasticsearch = require('elasticsearch') const NodeCache = require('node-cache') @@ -444,6 +445,22 @@ async function createResource (challengeId, memberHandle, roleId) { return res || false } +function exponentialDelay (retryNumber = 0) { + const delay = Math.pow(2, retryNumber) * 200 + const randomSum = delay * 0.2 * Math.random() // 0-20% of the delay + return delay + randomSum +} + +axiosRetry(axios, { + retries: `${config.AXIOS_RETRIES}`, // number of retries + retryCondition: (e) => { + return e.config.url.indexOf('v5/projects') > 0 && (axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429); + }, + onRetry: (retryCount, error, requestConfig) => + logger.info(retryCount), + retryDelay: exponentialDelay +}); + /** * Create Project * @param {String} name The name From 713faa15b7d64b1fb1192bfcad61440e9239ec5d Mon Sep 17 00:00:00 2001 From: eisbilir Date: Sun, 15 Jan 2023 17:05:42 +0300 Subject: [PATCH 4/4] update axios retry log --- src/common/helper.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/common/helper.js b/src/common/helper.js index eb8dad02..e5cddf7a 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -454,12 +454,13 @@ function exponentialDelay (retryNumber = 0) { axiosRetry(axios, { retries: `${config.AXIOS_RETRIES}`, // number of retries retryCondition: (e) => { - return e.config.url.indexOf('v5/projects') > 0 && (axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === 429); + return e.config.url.indexOf('v5/projects') > 0 && + (axiosRetry.isNetworkOrIdempotentRequestError(e) || e.response.status === HttpStatus.TOO_MANY_REQUESTS) }, - onRetry: (retryCount, error, requestConfig) => - logger.info(retryCount), + onRetry: (retryCount, error, requestConfig) => + logger.info(`${error.message} while calling: ${requestConfig.url} - retry count: ${retryCount}`), retryDelay: exponentialDelay -}); +}) /** * Create Project @@ -624,7 +625,7 @@ async function updateSelfServiceProjectInfo (projectId, workItemPlannedEndDate, const payment = await getProjectPayment(projectId) const token = await getM2MToken() const url = `${config.PROJECTS_API_URL}/${projectId}` - const res = await axios.patch(url, { + await axios.patch(url, { details: { ...project.details, paymentProvider: config.DEFAULT_PAYMENT_PROVIDER,