From d0924786a9274d655f19393033af26c369bf4115 Mon Sep 17 00:00:00 2001 From: Vitaly Aminev Date: Wed, 30 Oct 2019 22:11:03 -0700 Subject: [PATCH 01/13] feat: upgrade deps for node 12.13.0 (#432) BREAKING CHANGE: removes scrypt for built-in crypto implementation, while API remains the same the underlaying library is slightly different. Node has changed a major version, too --- .mdeprc | 4 +- package.json | 41 +- src/actions/.eslintrc | 5 + src/actions/activate.js | 4 +- src/actions/alias.js | 6 +- src/actions/ban.js | 2 +- src/actions/challenge.js | 4 +- src/actions/disposable-password.js | 6 +- src/actions/getMetadata.js | 2 +- src/actions/list.js | 2 +- src/actions/login.js | 8 +- src/actions/mfa/attach.js | 2 +- src/actions/mfa/detach.js | 4 +- src/actions/mfa/regenerate-codes.js | 2 +- src/actions/oauth/facebook.js | 2 +- src/actions/organization/create.js | 6 +- src/actions/organization/delete.js | 2 +- src/actions/organization/invites/accept.js | 4 +- src/actions/organization/invites/send.js | 6 +- src/actions/organization/list.js | 2 +- src/actions/organization/members/add.js | 2 +- src/actions/organization/members/list.js | 3 +- .../organization/members/permission.js | 4 +- src/actions/organization/members/remove.js | 4 +- src/actions/organization/updateMetadata.js | 2 +- src/actions/register.js | 14 +- src/actions/remove.js | 4 +- src/actions/requestPassword.js | 8 +- src/actions/token/create.js | 2 +- src/actions/token/erase.js | 2 +- src/actions/token/list.js | 2 +- src/actions/updateMetadata.js | 2 +- src/actions/updatePassword.js | 6 +- src/actions/verify.js | 2 +- src/auth/oauth/index.js | 2 +- .../oauth/{preResponse.js => pre-response.js} | 2 +- src/auth/oauth/utils/attach.js | 4 +- src/auth/oauth/utils/detach.js | 4 +- src/auth/oauth/utils/errors.js | 3 + .../utils/{extractJWT.js => extract-jwt.js} | 0 ...{getSignedToken.js => get-signed-token.js} | 0 src/configs/router.js | 2 +- src/custom/cappasity-users-activate.js | 2 +- src/custom/rfx-create-room-on-activate.js | 2 +- src/migrations/generate-users-ids/index.js | 2 +- src/users.js | 2 +- src/utils/{aliasExists.js => alias-exists.js} | 0 src/utils/challenges/email/generate.js | 2 +- .../{checkCaptcha.js => check-captcha.js} | 2 +- .../{checkIpLimits.js => check-ip-limits.js} | 2 +- ...nerateBacklink.js => generate-backlink.js} | 0 src/utils/{getMetadata.js => get-metadata.js} | 0 .../{hasNotPassword.js => has-no-password.js} | 0 src/utils/{hasPassword.js => has-password.js} | 0 src/utils/{isActive.js => is-active.js} | 0 src/utils/{isBanned.js => is-banned.js} | 0 .../{isDisposable.js => is-disposable.js} | 0 src/utils/jwt.js | 2 +- src/utils/{mxExists.js => mx-exists.js} | 0 ...Members.js => add-organization-members.js} | 10 +- ...Exists.js => check-organization-exists.js} | 2 +- ...etInternalData.js => get-internal-data.js} | 2 +- ...ganizationId.js => get-organization-id.js} | 0 ...Members.js => get-organization-members.js} | 0 ... get-organization-metadata-and-members.js} | 6 +- ...tadata.js => get-organization-metadata.js} | 2 +- src/utils/organization/index.js | 14 +- ...rs.js => register-organization-members.js} | 4 +- ...onData.js => resolve-organization-data.js} | 0 ...sendInviteMail.js => send-invite-email.js} | 0 ...wordValidator.js => password-validator.js} | 0 .../{pipelineError.js => pipeline-error.js} | 0 src/utils/{safeParse.js => safe-parse.js} | 0 src/utils/scrypt.js | 55 +- ...tadata.js => set-organization-metadata.js} | 8 +- .../{updateMetadata.js => update-metadata.js} | 8 +- ...etInternalData.js => get-internal-data.js} | 4 +- .../userData/{getUserId.js => get-user-id.js} | 4 +- src/utils/userData/index.js | 6 +- .../{resolveUserId.js => resolve-user-id.js} | 0 test/config.js | 4 +- test/docker-compose.sentinel.yml | 2 +- test/docker-compose.yml | 4 +- ...mpleDispatcher.js => simple-dispatcher.js} | 0 .../{accessTokens.js => access-tokens.js} | 0 test/suites/admins.js | 2 +- .../{getMetadata.js => get-metadata.js} | 0 test/suites/invite.js | 2 +- test/suites/organization/create.js | 2 +- .../{getMetadata.js => get-metadata.js} | 0 .../{updateMetadata.js => update-metadata.js} | 0 test/suites/remove.js | 2 +- ...requestPassword.js => request-password.js} | 0 .../{updateMetadata.js => update-metadata.js} | 2 +- .../{updatePassword.js => update-password.js} | 2 +- test/suites/verify.js | 2 +- yarn.lock | 1512 ++++++++++------- 97 files changed, 1062 insertions(+), 803 deletions(-) create mode 100644 src/actions/.eslintrc rename src/auth/oauth/{preResponse.js => pre-response.js} (97%) rename src/auth/oauth/utils/{extractJWT.js => extract-jwt.js} (100%) rename src/auth/oauth/utils/{getSignedToken.js => get-signed-token.js} (100%) rename src/utils/{aliasExists.js => alias-exists.js} (100%) rename src/utils/{checkCaptcha.js => check-captcha.js} (95%) rename src/utils/{checkIpLimits.js => check-ip-limits.js} (94%) rename src/utils/{generateBacklink.js => generate-backlink.js} (100%) rename src/utils/{getMetadata.js => get-metadata.js} (100%) rename src/utils/{hasNotPassword.js => has-no-password.js} (100%) rename src/utils/{hasPassword.js => has-password.js} (100%) rename src/utils/{isActive.js => is-active.js} (100%) rename src/utils/{isBanned.js => is-banned.js} (100%) rename src/utils/{isDisposable.js => is-disposable.js} (100%) rename src/utils/{mxExists.js => mx-exists.js} (100%) rename src/utils/organization/{addOrganizationMembers.js => add-organization-members.js} (90%) rename src/utils/organization/{checkOrganizationExists.js => check-organization-exists.js} (78%) rename src/utils/organization/{getInternalData.js => get-internal-data.js} (93%) rename src/utils/organization/{getOrganizationId.js => get-organization-id.js} (100%) rename src/utils/organization/{getOrganizationMembers.js => get-organization-members.js} (100%) rename src/utils/organization/{getOrganizationMetadataAndMembers.js => get-organization-metadata-and-members.js} (67%) rename src/utils/organization/{getOrganizationMetadata.js => get-organization-metadata.js} (92%) rename src/utils/organization/{registerOrganizationMembers.js => register-organization-members.js} (94%) rename src/utils/organization/{resolveOrganizationData.js => resolve-organization-data.js} (100%) rename src/utils/organization/{sendInviteMail.js => send-invite-email.js} (100%) rename src/utils/{passwordValidator.js => password-validator.js} (100%) rename src/utils/{pipelineError.js => pipeline-error.js} (100%) rename src/utils/{safeParse.js => safe-parse.js} (100%) rename src/utils/{setOrganizationMetadata.js => set-organization-metadata.js} (81%) rename src/utils/{updateMetadata.js => update-metadata.js} (94%) rename src/utils/userData/{getInternalData.js => get-internal-data.js} (94%) rename src/utils/userData/{getUserId.js => get-user-id.js} (73%) rename src/utils/userData/{resolveUserId.js => resolve-user-id.js} (100%) rename test/helpers/{simpleDispatcher.js => simple-dispatcher.js} (100%) rename test/suites/{accessTokens.js => access-tokens.js} (100%) rename test/suites/{getMetadata.js => get-metadata.js} (100%) rename test/suites/organization/{getMetadata.js => get-metadata.js} (100%) rename test/suites/organization/{updateMetadata.js => update-metadata.js} (100%) rename test/suites/{requestPassword.js => request-password.js} (100%) rename test/suites/{updateMetadata.js => update-metadata.js} (98%) rename test/suites/{updatePassword.js => update-password.js} (98%) diff --git a/.mdeprc b/.mdeprc index 445657cba..1f96cd71c 100644 --- a/.mdeprc +++ b/.mdeprc @@ -1,4 +1,4 @@ { - "node": "10.16.3", - "rebuild": ["ms-flakeless", "scrypt"] + "node": "12.13.0", + "rebuild": ["ms-flakeless"] } diff --git a/package.json b/package.json index 1e51f9043..f7694d524 100644 --- a/package.json +++ b/package.json @@ -31,19 +31,19 @@ "@hapi/bell": "^11.1.0", "@hapi/hapi": "^18.4.0", "@hapi/vision": "^5.5.4", - "@microfleet/core": "^15.0.1", + "@microfleet/core": "^15.3.1", "@microfleet/transport-amqp": "^15.0.0", "@microfleet/validation": "^8.1.2", - "bluebird": "^3.7.0", + "bluebird": "^3.7.1", "bytes": "^3.0.0", "common-errors": "^1.0.5", "csv-write-stream": "^2.0.0", - "disposable-email-domains": "^1.0.48", - "dlock": "^10.0.0", + "disposable-email-domains": "^1.0.49", + "dlock": "^11.0.0", "flake-idgen": "^1.1.0", "get-stdin": "^7.0.0", "get-value": "^3.0.1", - "handlebars": "^4.4.2", + "handlebars": "^4.5.1", "ioredis": "^4.14.1", "is": "^3.3.0", "jsonwebtoken": "^8.5.1", @@ -51,7 +51,7 @@ "lodash": "^4.17.15", "moment": "^2.23.0", "ms-conf": "^5.0.2", - "ms-flakeless": "^4.2.0", + "ms-flakeless": "^4.3.0", "ms-mailer-client": "^8.0.1", "ms-mailer-templates": "^1.14.1", "ms-token": "^3.1.0", @@ -62,26 +62,26 @@ "redis-filtered-sort": "^2.3.0", "request": "^2.88.0", "request-promise": "^4.2.4", - "scrypt": "^6.0.1", + "scrypt-kdf": "^2.0.1", "serialize-error": "^5.0.0", "serialize-javascript": "^2.1.0", "stdout-stream": "^1.4.1", "tough-cookie": "^3.0.0", "uuid": "^3.3.3", - "yargs": "^14.0.0", + "yargs": "^14.2.0", "zxcvbn": "^4.4.2" }, "devDependencies": { - "@babel/cli": "^7.6.2", - "@babel/core": "^7.6.2", + "@babel/cli": "^7.6.4", + "@babel/core": "^7.6.4", "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-proposal-object-rest-spread": "^7.6.2", "@babel/plugin-transform-strict-mode": "^7.2.0", "@babel/register": "^7.6.2", - "@makeomatic/deploy": "^9.1.1", - "@semantic-release/changelog": "^3.0.4", - "@semantic-release/exec": "^3.3.7", - "@semantic-release/git": "^7.0.16", + "@makeomatic/deploy": "^9.3.2", + "@semantic-release/changelog": "^3.0.5", + "@semantic-release/exec": "^3.3.8", + "@semantic-release/git": "^7.0.17", "apidoc": "^0.17.7", "apidoc-plugin-schema": "^0.1.8", "babel-eslint": "^10.0.3", @@ -90,18 +90,19 @@ "cheerio": "^1.0.0-rc.3", "codecov": "^3.6.1", "cross-env": "^6.0.3", - "eslint": "^6.5.1", - "eslint-config-makeomatic": "^3.1.0", + "eslint": "^6.6.0", + "eslint-config-makeomatic": "^4.0.0", "eslint-plugin-import": "^2.18.2", - "eslint-plugin-mocha": "^6.1.1", + "eslint-plugin-mocha": "^6.2.1", "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-unicorn": "^12.1.0", "faker": "^4.1.0", - "glob": "^7.1.4", + "glob": "^7.1.5", "json": "^9.0.6", "md5": "^2.2.1", - "mocha": "^6.2.1", + "mocha": "^6.2.2", "nyc": "^14.1.1", - "puppeteer": "1.20.0", + "puppeteer": "2.0.0", "rimraf": "^3.0.0", "sinon": "^7.5.0" }, diff --git a/src/actions/.eslintrc b/src/actions/.eslintrc new file mode 100644 index 000000000..46969a4fa --- /dev/null +++ b/src/actions/.eslintrc @@ -0,0 +1,5 @@ +{ + "rules": { + "unicorn/filename-case": 0 + } +} diff --git a/src/actions/activate.js b/src/actions/activate.js index 783f13029..007226bd8 100644 --- a/src/actions/activate.js +++ b/src/actions/activate.js @@ -3,8 +3,8 @@ const Promise = require('bluebird'); const redisKey = require('../utils/key.js'); const jwt = require('../utils/jwt.js'); const { getInternalData } = require('../utils/userData'); -const getMetadata = require('../utils/getMetadata'); -const handlePipeline = require('../utils/pipelineError.js'); +const getMetadata = require('../utils/get-metadata'); +const handlePipeline = require('../utils/pipeline-error.js'); const { USERS_INDEX, USERS_DATA, diff --git a/src/actions/alias.js b/src/actions/alias.js index 36309de31..9fb4855b0 100644 --- a/src/actions/alias.js +++ b/src/actions/alias.js @@ -2,10 +2,10 @@ const Promise = require('bluebird'); const Errors = require('common-errors'); const { ActionTransport } = require('@microfleet/core'); const { getInternalData } = require('../utils/userData'); -const isActive = require('../utils/isActive'); -const isBanned = require('../utils/isBanned'); +const isActive = require('../utils/is-active'); +const isBanned = require('../utils/is-banned'); const key = require('../utils/key'); -const handlePipeline = require('../utils/pipelineError'); +const handlePipeline = require('../utils/pipeline-error'); const { USERS_DATA, USERS_METADATA, diff --git a/src/actions/ban.js b/src/actions/ban.js index a2c102cd2..91e8355ce 100644 --- a/src/actions/ban.js +++ b/src/actions/ban.js @@ -3,7 +3,7 @@ const { ActionTransport } = require('@microfleet/core'); const mapValues = require('lodash/mapValues'); const redisKey = require('../utils/key.js'); const { getInternalData } = require('../utils/userData'); -const handlePipeline = require('../utils/pipelineError.js'); +const handlePipeline = require('../utils/pipeline-error'); const { USERS_DATA, USERS_METADATA, USERS_BANNED_FLAG, USERS_TOKENS, USERS_BANNED_DATA, diff --git a/src/actions/challenge.js b/src/actions/challenge.js index 1c7e778e3..46ee95f7f 100644 --- a/src/actions/challenge.js +++ b/src/actions/challenge.js @@ -2,8 +2,8 @@ const Promise = require('bluebird'); const passThrough = require('lodash/identity'); const { ActionTransport } = require('@microfleet/core'); const { getInternalData } = require('../utils/userData'); -const getMetadata = require('../utils/getMetadata'); -const isActive = require('../utils/isActive'); +const getMetadata = require('../utils/get-metadata'); +const isActive = require('../utils/is-active'); const challenge = require('../utils/challenges/challenge'); const { USERS_ACTION_ACTIVATE, diff --git a/src/actions/disposable-password.js b/src/actions/disposable-password.js index 3f9ed60e5..156da0e2c 100644 --- a/src/actions/disposable-password.js +++ b/src/actions/disposable-password.js @@ -2,9 +2,9 @@ const Promise = require('bluebird'); const { ActionTransport } = require('@microfleet/core'); const challenge = require('../utils/challenges/challenge'); const { getInternalData } = require('../utils/userData'); -const isActive = require('../utils/isActive'); -const isBanned = require('../utils/isBanned'); -const hasNotPassword = require('../utils/hasNotPassword'); +const isActive = require('../utils/is-active'); +const isBanned = require('../utils/is-banned'); +const hasNotPassword = require('../utils/has-no-password'); const { USERS_ACTION_DISPOSABLE_PASSWORD, USERS_USERNAME_FIELD } = require('../constants'); /** diff --git a/src/actions/getMetadata.js b/src/actions/getMetadata.js index e5dde6d68..d3e3cdd92 100644 --- a/src/actions/getMetadata.js +++ b/src/actions/getMetadata.js @@ -4,7 +4,7 @@ const Errors = require('common-errors'); const noop = require('lodash/noop'); const identity = require('lodash/identity'); const get = require('../utils/get-value'); -const getMetadata = require('../utils/getMetadata'); +const getMetadata = require('../utils/get-metadata'); const { getUserId } = require('../utils/userData'); const { USERS_ALIAS_FIELD } = require('../constants'); diff --git a/src/actions/list.js b/src/actions/list.js index 7d2f638bf..d03cb8950 100644 --- a/src/actions/list.js +++ b/src/actions/list.js @@ -3,7 +3,7 @@ const Promise = require('bluebird'); const mapValues = require('lodash/mapValues'); const passThrough = require('lodash/identity'); const fsort = require('redis-filtered-sort'); -const handlePipeline = require('../utils/pipelineError'); +const handlePipeline = require('../utils/pipeline-error'); const redisKey = require('../utils/key'); const { USERS_INDEX, diff --git a/src/actions/login.js b/src/actions/login.js index ad1a7d929..2b3e9aa85 100644 --- a/src/actions/login.js +++ b/src/actions/login.js @@ -7,11 +7,11 @@ const is = require('is'); const scrypt = require('../utils/scrypt'); const redisKey = require('../utils/key'); const jwt = require('../utils/jwt'); -const isActive = require('../utils/isActive'); -const isBanned = require('../utils/isBanned'); -const handlePipeline = require('../utils/pipelineError'); +const isActive = require('../utils/is-active'); +const isBanned = require('../utils/is-banned'); +const handlePipeline = require('../utils/pipeline-error'); const { checkMFA } = require('../utils/mfa'); -const { verifySignedToken } = require('../auth/oauth/utils/getSignedToken'); +const { verifySignedToken } = require('../auth/oauth/utils/get-signed-token'); const { USERS_ACTION_DISPOSABLE_PASSWORD, USERS_DISPOSABLE_PASSWORD_MIA, diff --git a/src/actions/mfa/attach.js b/src/actions/mfa/attach.js index fa9a4ba7c..b9c7720bf 100644 --- a/src/actions/mfa/attach.js +++ b/src/actions/mfa/attach.js @@ -2,7 +2,7 @@ const { ActionTransport } = require('@microfleet/core'); const Promise = require('bluebird'); const redisKey = require('../../utils/key'); -const handlePipeline = require('../../utils/pipelineError'); +const handlePipeline = require('../../utils/pipeline-error'); const { checkMFA, generateRecoveryCodes } = require('../../utils/mfa'); const { USERS_DATA, diff --git a/src/actions/mfa/detach.js b/src/actions/mfa/detach.js index 2150321ab..11597ef5b 100644 --- a/src/actions/mfa/detach.js +++ b/src/actions/mfa/detach.js @@ -1,8 +1,8 @@ const { ActionTransport } = require('@microfleet/core'); const Promise = require('bluebird'); const redisKey = require('../../utils/key'); -const handlePipeline = require('../../utils/pipelineError'); -const { checkMFA } = require('../../utils/mfa.js'); +const handlePipeline = require('../../utils/pipeline-error'); +const { checkMFA } = require('../../utils/mfa'); const { USERS_DATA, USERS_MFA_FLAG, diff --git a/src/actions/mfa/regenerate-codes.js b/src/actions/mfa/regenerate-codes.js index a3c1f76dc..5f8e759ea 100644 --- a/src/actions/mfa/regenerate-codes.js +++ b/src/actions/mfa/regenerate-codes.js @@ -1,7 +1,7 @@ const { ActionTransport } = require('@microfleet/core'); const Promise = require('bluebird'); const redisKey = require('../../utils/key'); -const handlePipeline = require('../../utils/pipelineError'); +const handlePipeline = require('../../utils/pipeline-error'); const { checkMFA, generateRecoveryCodes } = require('../../utils/mfa'); const { USERS_MFA_RECOVERY, MFA_TYPE_REQUIRED } = require('../../constants'); diff --git a/src/actions/oauth/facebook.js b/src/actions/oauth/facebook.js index 42d9c9e3d..b6a351499 100644 --- a/src/actions/oauth/facebook.js +++ b/src/actions/oauth/facebook.js @@ -2,7 +2,7 @@ const { ActionTransport } = require('@microfleet/core'); const Promise = require('bluebird'); const { ERROR_AUTH_REQUIRED } = require('../../constants'); const attach = require('../../auth/oauth/utils/attach'); -const { getSignedToken } = require('../../auth/oauth/utils/getSignedToken'); +const { getSignedToken } = require('../../auth/oauth/utils/get-signed-token'); async function facebookCallbackAction(request) { const { credentials } = request.auth; diff --git a/src/actions/organization/create.js b/src/actions/organization/create.js index 8fcb89605..83b74bf78 100644 --- a/src/actions/organization/create.js +++ b/src/actions/organization/create.js @@ -2,9 +2,9 @@ const { ActionTransport } = require('@microfleet/core'); const snakeCase = require('lodash/snakeCase'); const mapValues = require('lodash/mapValues'); const redisKey = require('../../utils/key'); -const handlePipeline = require('../../utils/pipelineError'); -const setOrganizationMetadata = require('../../utils/setOrganizationMetadata'); -const addOrganizationMembers = require('../../utils/organization/addOrganizationMembers'); +const handlePipeline = require('../../utils/pipeline-error'); +const setOrganizationMetadata = require('../../utils/set-organization-metadata'); +const addOrganizationMembers = require('../../utils/organization/add-organization-members'); const { getOrganizationId, getOrganizationMetadataAndMembers } = require('../../utils/organization'); const { ErrorConflictOrganizationExists, diff --git a/src/actions/organization/delete.js b/src/actions/organization/delete.js index 349a785f9..76065b4bf 100644 --- a/src/actions/organization/delete.js +++ b/src/actions/organization/delete.js @@ -1,7 +1,7 @@ const { ActionTransport } = require('@microfleet/core'); const snakeCase = require('lodash/snakeCase'); const redisKey = require('../../utils/key'); -const handlePipeline = require('../../utils/pipelineError'); +const handlePipeline = require('../../utils/pipeline-error'); const { checkOrganizationExists, getInternalData } = require('../../utils/organization'); const { ORGANIZATIONS_DATA, diff --git a/src/actions/organization/invites/accept.js b/src/actions/organization/invites/accept.js index 1c2a17dcb..9d9a51a77 100644 --- a/src/actions/organization/invites/accept.js +++ b/src/actions/organization/invites/accept.js @@ -6,8 +6,8 @@ const { USERS_ACTION_ORGANIZATION_INVITE, ErrorInvitationExpiredOrUsed, } = require('../../../constants'); -const redisKey = require('../../../utils/key.js'); -const getUserId = require('../../../utils/userData/getUserId'); +const redisKey = require('../../../utils/key'); +const getUserId = require('../../../utils/userData/get-user-id'); /** * Token verification function, on top of it returns extra metadata diff --git a/src/actions/organization/invites/send.js b/src/actions/organization/invites/send.js index 4ddf0de4d..3f215ab21 100644 --- a/src/actions/organization/invites/send.js +++ b/src/actions/organization/invites/send.js @@ -1,6 +1,6 @@ const { ActionTransport } = require('@microfleet/core'); -const sendInviteMail = require('../../../utils/organization/sendInviteMail'); -const getInternalData = require('../../../utils/organization/getInternalData'); +const sendInviteMail = require('../../../utils/organization/send-invite-email'); +const getInternalData = require('../../../utils/organization/get-internal-data'); const redisKey = require('../../../utils/key'); const { checkOrganizationExists } = require('../../../utils/organization'); const { @@ -10,7 +10,7 @@ const { ORGANIZATIONS_ID_FIELD, USERS_ACTION_ORGANIZATION_INVITE, } = require('../../../constants'); -const getUserId = require('../../../utils/userData/getUserId'); +const getUserId = require('../../../utils/userData/get-user-id'); /** * @api {amqp} .invites.send Send invitation diff --git a/src/actions/organization/list.js b/src/actions/organization/list.js index 38ba6d567..c1fd0d6df 100644 --- a/src/actions/organization/list.js +++ b/src/actions/organization/list.js @@ -3,7 +3,7 @@ const Promise = require('bluebird'); const fsort = require('redis-filtered-sort'); const redisKey = require('../../utils/key'); const { getOrganizationMetadata, getInternalData } = require('../../utils/organization'); -const { ORGANIZATIONS_INDEX, ORGANIZATIONS_DATA } = require('../../constants.js'); +const { ORGANIZATIONS_INDEX, ORGANIZATIONS_DATA } = require('../../constants'); /** * @api {amqp} .list Retrieve Organizations list diff --git a/src/actions/organization/members/add.js b/src/actions/organization/members/add.js index 61000fac7..7198fb40a 100644 --- a/src/actions/organization/members/add.js +++ b/src/actions/organization/members/add.js @@ -1,6 +1,6 @@ const { ActionTransport } = require('@microfleet/core'); const { checkOrganizationExists } = require('../../../utils/organization'); -const addOrganizationMembers = require('../../../utils/organization/addOrganizationMembers'); +const addOrganizationMembers = require('../../../utils/organization/add-organization-members'); /** * @api {amqp} .members.add Add organization member diff --git a/src/actions/organization/members/list.js b/src/actions/organization/members/list.js index 20c529663..f3816abbe 100644 --- a/src/actions/organization/members/list.js +++ b/src/actions/organization/members/list.js @@ -1,6 +1,5 @@ const { ActionTransport } = require('@microfleet/core'); -const { checkOrganizationExists } = require('../../../utils/organization'); -const { getOrganizationMembers } = require('../../../utils/organization'); +const { checkOrganizationExists, getOrganizationMembers } = require('../../../utils/organization'); /** * @api {amqp} .members.list Get organization members diff --git a/src/actions/organization/members/permission.js b/src/actions/organization/members/permission.js index 5ea5e4199..4e7e96eb7 100644 --- a/src/actions/organization/members/permission.js +++ b/src/actions/organization/members/permission.js @@ -3,8 +3,8 @@ const union = require('lodash/union'); const difference = require('lodash/difference'); const { checkOrganizationExists } = require('../../../utils/organization'); const redisKey = require('../../../utils/key'); -const handlePipeline = require('../../../utils/pipelineError'); -const getUserId = require('../../../utils/userData/getUserId'); +const handlePipeline = require('../../../utils/pipeline-error'); +const getUserId = require('../../../utils/userData/get-user-id'); const { ErrorUserNotMember, USERS_METADATA, ORGANIZATIONS_MEMBERS } = require('../../../constants'); /** diff --git a/src/actions/organization/members/remove.js b/src/actions/organization/members/remove.js index 57b9a409f..61437cd59 100644 --- a/src/actions/organization/members/remove.js +++ b/src/actions/organization/members/remove.js @@ -1,7 +1,7 @@ const { ActionTransport } = require('@microfleet/core'); const redisKey = require('../../../utils/key'); -const getUserId = require('../../../utils/userData/getUserId'); -const handlePipeline = require('../../../utils/pipelineError'); +const getUserId = require('../../../utils/userData/get-user-id'); +const handlePipeline = require('../../../utils/pipeline-error'); const { checkOrganizationExists } = require('../../../utils/organization'); const { ORGANIZATIONS_MEMBERS, diff --git a/src/actions/organization/updateMetadata.js b/src/actions/organization/updateMetadata.js index 2a5186e28..ac2e2a8fd 100644 --- a/src/actions/organization/updateMetadata.js +++ b/src/actions/organization/updateMetadata.js @@ -1,5 +1,5 @@ const { ActionTransport } = require('@microfleet/core'); -const setOrganizationMetadata = require('../../utils/setOrganizationMetadata'); +const setOrganizationMetadata = require('../../utils/set-organization-metadata'); const { checkOrganizationExists, getOrganizationMetadata } = require('../../utils/organization'); /** diff --git a/src/actions/register.js b/src/actions/register.js index ba0413630..e7f5347fc 100644 --- a/src/actions/register.js +++ b/src/actions/register.js @@ -9,18 +9,18 @@ const reduce = require('lodash/reduce'); const last = require('lodash/last'); // internal deps -const setMetadata = require('../utils/updateMetadata'); +const setMetadata = require('../utils/update-metadata'); const redisKey = require('../utils/key'); const jwt = require('../utils/jwt'); -const isDisposable = require('../utils/isDisposable'); -const mxExists = require('../utils/mxExists'); -const checkCaptcha = require('../utils/checkCaptcha'); +const isDisposable = require('../utils/is-disposable'); +const mxExists = require('../utils/mx-exists'); +const checkCaptcha = require('../utils/check-captcha'); const { getUserId } = require('../utils/userData'); -const aliasExists = require('../utils/aliasExists'); +const aliasExists = require('../utils/alias-exists'); const assignAlias = require('./alias'); -const checkLimits = require('../utils/checkIpLimits'); +const checkLimits = require('../utils/check-ip-limits'); const challenge = require('../utils/challenges/challenge'); -const handlePipeline = require('../utils/pipelineError'); +const handlePipeline = require('../utils/pipeline-error'); const hashPassword = require('../utils/register/password/hash'); const { USERS_REF, diff --git a/src/actions/remove.js b/src/actions/remove.js index cdad191e2..0951c03bf 100644 --- a/src/actions/remove.js +++ b/src/actions/remove.js @@ -5,8 +5,8 @@ const intersection = require('lodash/intersection'); const get = require('../utils/get-value'); const key = require('../utils/key'); const { getInternalData } = require('../utils/userData'); -const getMetadata = require('../utils/getMetadata'); -const handlePipeline = require('../utils/pipelineError'); +const getMetadata = require('../utils/get-metadata'); +const handlePipeline = require('../utils/pipeline-error'); const { USERS_INDEX, USERS_PUBLIC_INDEX, diff --git a/src/actions/requestPassword.js b/src/actions/requestPassword.js index 3eccce369..9f10b89e1 100644 --- a/src/actions/requestPassword.js +++ b/src/actions/requestPassword.js @@ -1,9 +1,9 @@ const Promise = require('bluebird'); const { getInternalData } = require('../utils/userData'); -const isActive = require('../utils/isActive.js'); -const isBanned = require('../utils/isBanned.js'); -const hasPassword = require('../utils/hasPassword.js'); -const getMetadata = require('../utils/getMetadata.js'); +const isActive = require('../utils/is-active'); +const isBanned = require('../utils/is-banned'); +const hasPassword = require('../utils/has-password'); +const getMetadata = require('../utils/get-metadata'); const challenge = require('../utils/challenges/challenge'); const { USERS_ACTION_PASSWORD, diff --git a/src/actions/token/create.js b/src/actions/token/create.js index e83ebd6fa..2f7fb0c44 100644 --- a/src/actions/token/create.js +++ b/src/actions/token/create.js @@ -3,7 +3,7 @@ const Promise = require('bluebird'); const { ActionTransport } = require('@microfleet/core'); const { sign } = require('../../utils/signatures'); const redisKey = require('../../utils/key'); -const handlePipelineError = require('../../utils/pipelineError'); +const handlePipelineError = require('../../utils/pipeline-error'); const { getUserId } = require('../../utils/userData'); const { USERS_API_TOKENS, USERS_API_TOKENS_ZSET, BEARER_USERNAME_FIELD } = require('../../constants'); diff --git a/src/actions/token/erase.js b/src/actions/token/erase.js index 4be2eb085..a90cc4578 100644 --- a/src/actions/token/erase.js +++ b/src/actions/token/erase.js @@ -1,6 +1,6 @@ const Promise = require('bluebird'); const redisKey = require('../../utils/key'); -const handlePipelineError = require('../../utils/pipelineError'); +const handlePipelineError = require('../../utils/pipeline-error'); const { USERS_API_TOKENS, USERS_API_TOKENS_ZSET } = require('../../constants'); const { getUserId } = require('../../utils/userData'); diff --git a/src/actions/token/list.js b/src/actions/token/list.js index 4971aecd7..8c1f16613 100644 --- a/src/actions/token/list.js +++ b/src/actions/token/list.js @@ -1,6 +1,6 @@ const Promise = require('bluebird'); const redisKey = require('../../utils/key'); -const handlePipelineError = require('../../utils/pipelineError'); +const handlePipelineError = require('../../utils/pipeline-error'); const { USERS_API_TOKENS_ZSET, USERS_API_TOKENS } = require('../../constants'); const { getUserId } = require('../../utils/userData'); diff --git a/src/actions/updateMetadata.js b/src/actions/updateMetadata.js index 450762c99..e6ede60b7 100644 --- a/src/actions/updateMetadata.js +++ b/src/actions/updateMetadata.js @@ -1,6 +1,6 @@ const omit = require('lodash/omit'); const Promise = require('bluebird'); -const updateMetadata = require('../utils/updateMetadata.js'); +const updateMetadata = require('../utils/update-metadata'); const { getUserId } = require('../utils/userData'); /** diff --git a/src/actions/updatePassword.js b/src/actions/updatePassword.js index 18ec3203a..8c3d0082c 100644 --- a/src/actions/updatePassword.js +++ b/src/actions/updatePassword.js @@ -5,9 +5,9 @@ const scrypt = require('../utils/scrypt'); const redisKey = require('../utils/key'); const jwt = require('../utils/jwt'); const { getInternalData } = require('../utils/userData'); -const isActive = require('../utils/isActive'); -const isBanned = require('../utils/isBanned'); -const hasPassword = require('../utils/hasPassword'); +const isActive = require('../utils/is-active'); +const isBanned = require('../utils/is-banned'); +const hasPassword = require('../utils/has-password'); const { getUserId } = require('../utils/userData'); const { USERS_DATA, diff --git a/src/actions/verify.js b/src/actions/verify.js index 218ca9568..250e0262a 100644 --- a/src/actions/verify.js +++ b/src/actions/verify.js @@ -2,7 +2,7 @@ const Promise = require('bluebird'); const { HttpStatusError } = require('common-errors'); const { ActionTransport } = require('@microfleet/core'); const jwt = require('../utils/jwt'); -const getMetadata = require('../utils/getMetadata'); +const getMetadata = require('../utils/get-metadata'); const { getInternalData } = require('../utils/userData'); const { USERS_MFA_FLAG } = require('../constants'); diff --git a/src/auth/oauth/index.js b/src/auth/oauth/index.js index 15cc79418..430018e78 100644 --- a/src/auth/oauth/index.js +++ b/src/auth/oauth/index.js @@ -5,7 +5,7 @@ const Boom = require('@hapi/boom'); const get = require('../../utils/get-value'); const getUid = require('./utils/uid'); const refresh = require('./utils/refresh'); -const extractJWT = require('./utils/extractJWT'); +const extractJWT = require('./utils/extract-jwt'); const { getInternalData } = require('../../utils/userData'); const { verifyToken, loginAttempt } = require('../../utils/amqp'); diff --git a/src/auth/oauth/preResponse.js b/src/auth/oauth/pre-response.js similarity index 97% rename from src/auth/oauth/preResponse.js rename to src/auth/oauth/pre-response.js index 5ee5a3047..535b20bd5 100644 --- a/src/auth/oauth/preResponse.js +++ b/src/auth/oauth/pre-response.js @@ -7,7 +7,7 @@ const { ActionTransport } = require('@microfleet/core'); const { AuthenticationRequiredError } = require('common-errors'); const { Redirect } = require('./utils/errors'); const { ErrorTotpRequired } = require('../../constants'); -const { getSignedToken } = require('./utils/getSignedToken'); +const { getSignedToken } = require('./utils/get-signed-token'); const isOauthAttachRoute = (route) => route.endsWith('oauth.facebook'); diff --git a/src/auth/oauth/utils/attach.js b/src/auth/oauth/utils/attach.js index 1e1569c44..6028ca7e1 100644 --- a/src/auth/oauth/utils/attach.js +++ b/src/auth/oauth/utils/attach.js @@ -1,7 +1,7 @@ const get = require('lodash/get'); const redisKey = require('../../../utils/key'); -const updateMetadata = require('../../../utils/updateMetadata'); -const handlePipeline = require('../../../utils/pipelineError'); +const updateMetadata = require('../../../utils/update-metadata'); +const handlePipeline = require('../../../utils/pipeline-error'); const { USERS_SSO_TO_ID, USERS_DATA, diff --git a/src/auth/oauth/utils/detach.js b/src/auth/oauth/utils/detach.js index f18cfd11f..8c4ea8530 100644 --- a/src/auth/oauth/utils/detach.js +++ b/src/auth/oauth/utils/detach.js @@ -2,8 +2,8 @@ const Errors = require('common-errors'); const get = require('../../../utils/get-value'); const redisKey = require('../../../utils/key'); -const updateMetadata = require('../../../utils/updateMetadata'); -const handlePipeline = require('../../../utils/pipelineError'); +const updateMetadata = require('../../../utils/update-metadata'); +const handlePipeline = require('../../../utils/pipeline-error'); const { USERS_SSO_TO_ID, diff --git a/src/auth/oauth/utils/errors.js b/src/auth/oauth/utils/errors.js index 8e107eafb..1bf62dd71 100644 --- a/src/auth/oauth/utils/errors.js +++ b/src/auth/oauth/utils/errors.js @@ -3,6 +3,9 @@ const Boom = require('@hapi/boom'); exports.Redirect = Errors.helpers.generateClass('Redirect', { args: ['redirectUri'], + generateMessage() { + return `${this.name}: ${this.redirectUri}`; + }, }); const OAuthError = exports.OAuthError = Errors.helpers.generateClass('OAuthError', { diff --git a/src/auth/oauth/utils/extractJWT.js b/src/auth/oauth/utils/extract-jwt.js similarity index 100% rename from src/auth/oauth/utils/extractJWT.js rename to src/auth/oauth/utils/extract-jwt.js diff --git a/src/auth/oauth/utils/getSignedToken.js b/src/auth/oauth/utils/get-signed-token.js similarity index 100% rename from src/auth/oauth/utils/getSignedToken.js rename to src/auth/oauth/utils/get-signed-token.js diff --git a/src/configs/router.js b/src/configs/router.js index e610de86e..7f8c79c97 100644 --- a/src/configs/router.js +++ b/src/configs/router.js @@ -28,7 +28,7 @@ const metrics = routerExtension('audit/metrics'); * Catches errors from oauth.facebook and wraps them into HTML * @type {Function} */ -const preResponse = require('../auth/oauth/preResponse'); +const preResponse = require('../auth/oauth/pre-response'); /** diff --git a/src/custom/cappasity-users-activate.js b/src/custom/cappasity-users-activate.js index ab9826edd..94ac4faa4 100644 --- a/src/custom/cappasity-users-activate.js +++ b/src/custom/cappasity-users-activate.js @@ -1,6 +1,6 @@ const find = require('lodash/find'); const moment = require('moment'); -const setMetadata = require('../utils/updateMetadata.js'); +const setMetadata = require('../utils/update-metadata'); /** * Adds metadata from billing into usermix diff --git a/src/custom/rfx-create-room-on-activate.js b/src/custom/rfx-create-room-on-activate.js index 10429ce16..81bcfa779 100644 --- a/src/custom/rfx-create-room-on-activate.js +++ b/src/custom/rfx-create-room-on-activate.js @@ -1,6 +1,6 @@ const is = require('is'); const Promise = require('bluebird'); -const setMetadata = require('../utils/updateMetadata.js'); +const setMetadata = require('../utils/update-metadata'); /** * @param {String} username diff --git a/src/migrations/generate-users-ids/index.js b/src/migrations/generate-users-ids/index.js index 067eec126..589723cef 100644 --- a/src/migrations/generate-users-ids/index.js +++ b/src/migrations/generate-users-ids/index.js @@ -18,7 +18,7 @@ const { USERS_USERNAME_FIELD, } = require('../../constants'); const makeKey = require('../../utils/key'); -const safeParse = require('../../utils/safeParse'); +const safeParse = require('../../utils/safe-parse'); function generateUsersIds({ flake, redis, config, log, diff --git a/src/users.js b/src/users.js index 233754475..18da85029 100644 --- a/src/users.js +++ b/src/users.js @@ -9,7 +9,7 @@ const RedisCluster = require('ioredis').Cluster; const Flakeless = require('ms-flakeless'); const conf = require('./config'); const get = require('./utils/get-value'); -const attachPasswordKeyword = require('./utils/passwordValidator.js'); +const attachPasswordKeyword = require('./utils/password-validator'); /** * @namespace Users diff --git a/src/utils/aliasExists.js b/src/utils/alias-exists.js similarity index 100% rename from src/utils/aliasExists.js rename to src/utils/alias-exists.js diff --git a/src/utils/challenges/email/generate.js b/src/utils/challenges/email/generate.js index 25a27c61b..9e2aa154f 100644 --- a/src/utils/challenges/email/generate.js +++ b/src/utils/challenges/email/generate.js @@ -6,7 +6,7 @@ const partial = require('lodash/partial'); const identity = require('lodash/identity'); const { InvalidOperationError } = require('common-errors'); const sendEmail = require('./send'); -const generateLink = require('../../generateBacklink'); +const generateLink = require('../../generate-backlink'); const { updatePassword } = require('../../../actions/updatePassword'); const { USERS_ACTION_ACTIVATE, diff --git a/src/utils/checkCaptcha.js b/src/utils/check-captcha.js similarity index 95% rename from src/utils/checkCaptcha.js rename to src/utils/check-captcha.js index f4347352c..9c42a6c7a 100644 --- a/src/utils/checkCaptcha.js +++ b/src/utils/check-captcha.js @@ -3,7 +3,7 @@ const request = require('request-promise'); const defaults = require('lodash/defaults'); const pick = require('lodash/pick'); const fmt = require('util').format; -const handlePipeline = require('../utils/pipelineError.js'); +const handlePipeline = require('../utils/pipeline-error'); /** * Performs captcha check, returns thukn diff --git a/src/utils/checkIpLimits.js b/src/utils/check-ip-limits.js similarity index 94% rename from src/utils/checkIpLimits.js rename to src/utils/check-ip-limits.js index 87b18a5fd..cd798bdcc 100644 --- a/src/utils/checkIpLimits.js +++ b/src/utils/check-ip-limits.js @@ -1,7 +1,7 @@ const { HttpStatusError } = require('common-errors'); const uuid = require('uuid/v4'); const redisKey = require('./key'); -const handlePipeline = require('../utils/pipelineError.js'); +const handlePipeline = require('../utils/pipeline-error'); /** * Verify ip limits diff --git a/src/utils/generateBacklink.js b/src/utils/generate-backlink.js similarity index 100% rename from src/utils/generateBacklink.js rename to src/utils/generate-backlink.js diff --git a/src/utils/getMetadata.js b/src/utils/get-metadata.js similarity index 100% rename from src/utils/getMetadata.js rename to src/utils/get-metadata.js diff --git a/src/utils/hasNotPassword.js b/src/utils/has-no-password.js similarity index 100% rename from src/utils/hasNotPassword.js rename to src/utils/has-no-password.js diff --git a/src/utils/hasPassword.js b/src/utils/has-password.js similarity index 100% rename from src/utils/hasPassword.js rename to src/utils/has-password.js diff --git a/src/utils/isActive.js b/src/utils/is-active.js similarity index 100% rename from src/utils/isActive.js rename to src/utils/is-active.js diff --git a/src/utils/isBanned.js b/src/utils/is-banned.js similarity index 100% rename from src/utils/isBanned.js rename to src/utils/is-banned.js diff --git a/src/utils/isDisposable.js b/src/utils/is-disposable.js similarity index 100% rename from src/utils/isDisposable.js rename to src/utils/is-disposable.js diff --git a/src/utils/jwt.js b/src/utils/jwt.js index e89198b59..49a386969 100644 --- a/src/utils/jwt.js +++ b/src/utils/jwt.js @@ -4,7 +4,7 @@ const jwt = Promise.promisifyAll(require('jsonwebtoken')); // internal modules const redisKey = require('./key'); -const getMetadata = require('../utils/getMetadata'); +const getMetadata = require('../utils/get-metadata'); const { verify: verifyHMAC } = require('./signatures'); const { USERS_TOKENS, diff --git a/src/utils/mxExists.js b/src/utils/mx-exists.js similarity index 100% rename from src/utils/mxExists.js rename to src/utils/mx-exists.js diff --git a/src/utils/organization/addOrganizationMembers.js b/src/utils/organization/add-organization-members.js similarity index 90% rename from src/utils/organization/addOrganizationMembers.js rename to src/utils/organization/add-organization-members.js index fdcde95a9..fa98eb300 100644 --- a/src/utils/organization/addOrganizationMembers.js +++ b/src/utils/organization/add-organization-members.js @@ -2,11 +2,11 @@ const Promise = require('bluebird'); const mapValues = require('lodash/mapValues'); const redisKey = require('../key.js'); -const getUserId = require('../userData/getUserId'); -const sendInviteMail = require('./sendInviteMail'); -const getInternalData = require('./getInternalData'); -const registerOrganizationMembers = require('./registerOrganizationMembers'); -const handlePipeline = require('../pipelineError.js'); +const getUserId = require('../userData/get-user-id'); +const sendInviteMail = require('./send-invite-email'); +const getInternalData = require('./get-internal-data'); +const registerOrganizationMembers = require('./register-organization-members'); +const handlePipeline = require('../pipeline-error'); const { ORGANIZATIONS_MEMBERS, USERS_METADATA, diff --git a/src/utils/organization/checkOrganizationExists.js b/src/utils/organization/check-organization-exists.js similarity index 78% rename from src/utils/organization/checkOrganizationExists.js rename to src/utils/organization/check-organization-exists.js index 4f5e1761d..f6be43a48 100644 --- a/src/utils/organization/checkOrganizationExists.js +++ b/src/utils/organization/check-organization-exists.js @@ -1,4 +1,4 @@ -const getInternalData = require('./getInternalData'); +const getInternalData = require('./get-internal-data'); async function checkOrganizationExists(request) { const { organizationId } = request.params; diff --git a/src/utils/organization/getInternalData.js b/src/utils/organization/get-internal-data.js similarity index 93% rename from src/utils/organization/getInternalData.js rename to src/utils/organization/get-internal-data.js index 5dba962c0..cab458a50 100644 --- a/src/utils/organization/getInternalData.js +++ b/src/utils/organization/get-internal-data.js @@ -1,5 +1,5 @@ const Promise = require('bluebird'); -const resolveOrganizationData = require('./resolveOrganizationData'); +const resolveOrganizationData = require('./resolve-organization-data'); const { ORGANIZATIONS_ID_FIELD, ErrorOrganizationNotFound } = require('../../constants'); const { hasOwnProperty } = Object.prototype; diff --git a/src/utils/organization/getOrganizationId.js b/src/utils/organization/get-organization-id.js similarity index 100% rename from src/utils/organization/getOrganizationId.js rename to src/utils/organization/get-organization-id.js diff --git a/src/utils/organization/getOrganizationMembers.js b/src/utils/organization/get-organization-members.js similarity index 100% rename from src/utils/organization/getOrganizationMembers.js rename to src/utils/organization/get-organization-members.js diff --git a/src/utils/organization/getOrganizationMetadataAndMembers.js b/src/utils/organization/get-organization-metadata-and-members.js similarity index 67% rename from src/utils/organization/getOrganizationMetadataAndMembers.js rename to src/utils/organization/get-organization-metadata-and-members.js index e6c95530b..9f217f7bf 100644 --- a/src/utils/organization/getOrganizationMetadataAndMembers.js +++ b/src/utils/organization/get-organization-metadata-and-members.js @@ -1,6 +1,6 @@ -const getInternalData = require('./getInternalData'); -const getOrganizationMembers = require('./getOrganizationMembers'); -const getOrganizationMetadata = require('./getOrganizationMetadata'); +const getInternalData = require('./get-internal-data'); +const getOrganizationMembers = require('./get-organization-members'); +const getOrganizationMetadata = require('./get-organization-metadata'); async function getOrganizationMetadataAndMembers(organizationId) { const organization = await getInternalData.call(this, organizationId, true); diff --git a/src/utils/organization/getOrganizationMetadata.js b/src/utils/organization/get-organization-metadata.js similarity index 92% rename from src/utils/organization/getOrganizationMetadata.js rename to src/utils/organization/get-organization-metadata.js index 3b0da4ddd..df980b422 100644 --- a/src/utils/organization/getOrganizationMetadata.js +++ b/src/utils/organization/get-organization-metadata.js @@ -1,7 +1,7 @@ const mapValues = require('lodash/mapValues'); const { ORGANIZATIONS_METADATA } = require('../../constants'); const redisKey = require('../key'); -const JSONParse = require('../safeParse'); +const JSONParse = require('../safe-parse'); async function getOrganizationMetadata(organizationId, audience) { const { redis, config } = this; diff --git a/src/utils/organization/index.js b/src/utils/organization/index.js index 284c2b41e..d53282deb 100644 --- a/src/utils/organization/index.js +++ b/src/utils/organization/index.js @@ -1,10 +1,10 @@ -const getOrganizationId = require('./getOrganizationId'); -const resolveOrganizationData = require('./resolveOrganizationData'); -const getInternalData = require('./getInternalData'); -const getOrganizationMetadataAndMembers = require('./getOrganizationMetadataAndMembers'); -const getOrganizationMembers = require('./getOrganizationMembers'); -const getOrganizationMetadata = require('./getOrganizationMetadata'); -const checkOrganizationExists = require('./checkOrganizationExists'); +const getOrganizationId = require('./get-organization-id'); +const resolveOrganizationData = require('./resolve-organization-data'); +const getInternalData = require('./get-internal-data'); +const getOrganizationMetadataAndMembers = require('./get-organization-metadata-and-members'); +const getOrganizationMembers = require('./get-organization-members'); +const getOrganizationMetadata = require('./get-organization-metadata'); +const checkOrganizationExists = require('./check-organization-exists'); module.exports = { getOrganizationId, diff --git a/src/utils/organization/registerOrganizationMembers.js b/src/utils/organization/register-organization-members.js similarity index 94% rename from src/utils/organization/registerOrganizationMembers.js rename to src/utils/organization/register-organization-members.js index a97c1fa2a..788a08e7f 100644 --- a/src/utils/organization/registerOrganizationMembers.js +++ b/src/utils/organization/register-organization-members.js @@ -2,7 +2,7 @@ const Promise = require('bluebird'); const generatePassword = require('password-generator'); const redisKey = require('../key.js'); -const handlePipeline = require('../pipelineError.js'); +const handlePipeline = require('../pipeline-error'); const { USERS_CREATED_FIELD, USERS_USERNAME_FIELD, @@ -14,7 +14,7 @@ const { USERS_ID_FIELD, } = require('../../constants.js'); const scrypt = require('../scrypt'); -const setMetadata = require('../updateMetadata'); +const setMetadata = require('../update-metadata'); async function registerOrganizationMember(member) { const { redis, config } = this; diff --git a/src/utils/organization/resolveOrganizationData.js b/src/utils/organization/resolve-organization-data.js similarity index 100% rename from src/utils/organization/resolveOrganizationData.js rename to src/utils/organization/resolve-organization-data.js diff --git a/src/utils/organization/sendInviteMail.js b/src/utils/organization/send-invite-email.js similarity index 100% rename from src/utils/organization/sendInviteMail.js rename to src/utils/organization/send-invite-email.js diff --git a/src/utils/passwordValidator.js b/src/utils/password-validator.js similarity index 100% rename from src/utils/passwordValidator.js rename to src/utils/password-validator.js diff --git a/src/utils/pipelineError.js b/src/utils/pipeline-error.js similarity index 100% rename from src/utils/pipelineError.js rename to src/utils/pipeline-error.js diff --git a/src/utils/safeParse.js b/src/utils/safe-parse.js similarity index 100% rename from src/utils/safeParse.js rename to src/utils/safe-parse.js diff --git a/src/utils/scrypt.js b/src/utils/scrypt.js index b4d204078..52ee94b9a 100644 --- a/src/utils/scrypt.js +++ b/src/utils/scrypt.js @@ -1,43 +1,38 @@ -const Errors = require('common-errors'); -const Promise = require('bluebird'); -const scrypt = Promise.promisifyAll(require('scrypt')); -const bytes = require('bytes'); +const { HttpStatusError } = require('common-errors'); +const Scrypt = require('scrypt-kdf'); const assert = require('assert'); +const bytes = require('bytes'); const { USERS_INCORRECT_PASSWORD } = require('../constants'); -// setup scrypt -const scryptParams = scrypt.paramsSync(0.1, bytes('32mb')); -const kUnacceptableVerificationMethod = new Errors.HttpStatusError(423, 'unacceptable verification method'); +const scryptParams = Scrypt.pickParams(0.1, bytes('32mb')); +const kUnacceptableVerificationMethod = new HttpStatusError(423, 'unacceptable verification method'); +const kInvalidPasswordPassedError = new HttpStatusError(500, 'invalid password passed'); -exports.hash = function hashPassword(password) { +exports.hash = async function hashPassword(password) { if (!password) { - throw new Errors.HttpStatusError(500, 'invalid password passed'); + throw kInvalidPasswordPassedError; } - return scrypt.kdfAsync(Buffer.from(password), scryptParams); + return Scrypt.kdf(password, scryptParams); }; -exports.verify = function verifyPassword(hash, password) { +exports.verify = async function verifyPassword(hash, password) { if (Buffer.isBuffer(hash) === false) { - return Promise.reject(kUnacceptableVerificationMethod); + throw kUnacceptableVerificationMethod; } - return Promise - .try(() => { - assert.ok(password, 'password arg must be present'); - return [hash, Buffer.from(password)]; - }) - .spread(scrypt.verifyKdfAsync) - .catch(function scryptError(err) { - throw new Errors.HttpStatusError(403, err.message || err.scrypt_err_message); - }) - .then(function verifyResult(result) { - if (result !== true) { - throw USERS_INCORRECT_PASSWORD; - } - - return result; - }); -}; + assert(password, 'password arg must be present'); + + let isValid = false; + try { + isValid = await Scrypt.verify(hash, password); + } catch (err) { + throw new HttpStatusError(403, err.message || err.scrypt_err_message); + } + + if (isValid !== true) { + throw USERS_INCORRECT_PASSWORD; + } -exports.scrypt = scrypt; + return isValid; +}; diff --git a/src/utils/setOrganizationMetadata.js b/src/utils/set-organization-metadata.js similarity index 81% rename from src/utils/setOrganizationMetadata.js rename to src/utils/set-organization-metadata.js index a9f9b47db..fce6659cf 100644 --- a/src/utils/setOrganizationMetadata.js +++ b/src/utils/set-organization-metadata.js @@ -2,10 +2,10 @@ const Promise = require('bluebird'); const is = require('is'); const { HttpStatusError } = require('common-errors'); -const redisKey = require('../utils/key.js'); -const handlePipeline = require('../utils/pipelineError.js'); -const { handleAudience } = require('../utils/updateMetadata.js'); -const { ORGANIZATIONS_METADATA } = require('../constants.js'); +const redisKey = require('../utils/key'); +const handlePipeline = require('../utils/pipeline-error'); +const { handleAudience } = require('../utils/update-metadata'); +const { ORGANIZATIONS_METADATA } = require('../constants'); /** * Updates metadata on a organization object diff --git a/src/utils/updateMetadata.js b/src/utils/update-metadata.js similarity index 94% rename from src/utils/updateMetadata.js rename to src/utils/update-metadata.js index 6fa5f56b8..f93ea9070 100644 --- a/src/utils/updateMetadata.js +++ b/src/utils/update-metadata.js @@ -3,10 +3,10 @@ const Promise = require('bluebird'); const mapValues = require('lodash/mapValues'); const is = require('is'); const { HttpStatusError } = require('common-errors'); -const redisKey = require('../utils/key.js'); -const sha256 = require('./sha256.js'); -const handlePipeline = require('../utils/pipelineError.js'); -const { USERS_METADATA } = require('../constants.js'); +const redisKey = require('../utils/key'); +const sha256 = require('./sha256'); +const handlePipeline = require('../utils/pipeline-error'); +const { USERS_METADATA } = require('../constants'); const JSONStringify = (data) => JSON.stringify(data); diff --git a/src/utils/userData/getInternalData.js b/src/utils/userData/get-internal-data.js similarity index 94% rename from src/utils/userData/getInternalData.js rename to src/utils/userData/get-internal-data.js index 4b6f5d2af..26f1f1704 100644 --- a/src/utils/userData/getInternalData.js +++ b/src/utils/userData/get-internal-data.js @@ -2,9 +2,9 @@ const { HttpStatusError } = require('common-errors'); const reduce = require('lodash/reduce'); const Promise = require('bluebird'); const zipObject = require('lodash/zipObject'); -const resolveUserId = require('./resolveUserId'); +const resolveUserId = require('./resolve-user-id'); const { USERS_PASSWORD_FIELD, USERS_ID_FIELD, FIELDS_TO_STRINGIFY } = require('../../constants'); -const safeParse = require('../safeParse'); +const safeParse = require('../safe-parse'); const { hasOwnProperty } = Object.prototype; const STRINGIFY_FIELDS = zipObject(FIELDS_TO_STRINGIFY); diff --git a/src/utils/userData/getUserId.js b/src/utils/userData/get-user-id.js similarity index 73% rename from src/utils/userData/getUserId.js rename to src/utils/userData/get-user-id.js index 1b13f3431..da926776a 100644 --- a/src/utils/userData/getUserId.js +++ b/src/utils/userData/get-user-id.js @@ -1,5 +1,5 @@ -const getInternalData = require('./getInternalData'); -const isBanned = require('../isBanned'); +const getInternalData = require('./get-internal-data'); +const isBanned = require('../is-banned'); async function getUserId(username, verifyBanned = false) { const internalData = await getInternalData diff --git a/src/utils/userData/index.js b/src/utils/userData/index.js index e944154a2..285545a89 100644 --- a/src/utils/userData/index.js +++ b/src/utils/userData/index.js @@ -1,6 +1,6 @@ -const getInternalData = require('./getInternalData'); -const getUserId = require('./getUserId'); -const resolveUserId = require('./resolveUserId'); +const getInternalData = require('./get-internal-data'); +const getUserId = require('./get-user-id'); +const resolveUserId = require('./resolve-user-id'); module.exports = { getInternalData, diff --git a/src/utils/userData/resolveUserId.js b/src/utils/userData/resolve-user-id.js similarity index 100% rename from src/utils/userData/resolveUserId.js rename to src/utils/userData/resolve-user-id.js diff --git a/test/config.js b/test/config.js index f931d4d86..65d74e212 100644 --- a/test/config.js +++ b/test/config.js @@ -1,5 +1,5 @@ const Promise = require('bluebird'); -const simpleDispatcher = require('./helpers/simpleDispatcher'); +const simpleDispatcher = require('./helpers/simple-dispatcher'); function registerUser(username, opts = {}) { return async function register() { @@ -69,7 +69,7 @@ async function clearRedis(doNotClose = false) { } } finally { if (doNotClose === false) { - await service.close().reflect(); + await service.close(); this.users = null; this.dispatch = null; } diff --git a/test/docker-compose.sentinel.yml b/test/docker-compose.sentinel.yml index 962b8d046..6dc58e4a1 100644 --- a/test/docker-compose.sentinel.yml +++ b/test/docker-compose.sentinel.yml @@ -76,7 +76,7 @@ services: shm_size: 512m expose: - "3000" - image: makeomatic/node:10.16.3-chrome-tester + image: makeomatic/node:12.13.0-chrome-tester links: - redis - rabbitmq diff --git a/test/docker-compose.yml b/test/docker-compose.yml index 690cff435..12718a0ea 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -16,7 +16,7 @@ networks: services: redis: hostname: redis - image: makeomatic/redis-cluster:3.2.9 + image: makeomatic/redis-cluster:5-alpine networks: static: ipv4_address: 172.16.238.10 @@ -60,7 +60,7 @@ services: shm_size: 512m expose: - "3000" - image: makeomatic/node:10.16.3-chrome-tester + image: makeomatic/node:12.13.0-chrome-tester links: - redis - rabbitmq diff --git a/test/helpers/simpleDispatcher.js b/test/helpers/simple-dispatcher.js similarity index 100% rename from test/helpers/simpleDispatcher.js rename to test/helpers/simple-dispatcher.js diff --git a/test/suites/accessTokens.js b/test/suites/access-tokens.js similarity index 100% rename from test/suites/accessTokens.js rename to test/suites/access-tokens.js diff --git a/test/suites/admins.js b/test/suites/admins.js index 34090484e..f1b9be81d 100644 --- a/test/suites/admins.js +++ b/test/suites/admins.js @@ -1,7 +1,7 @@ /* global startService */ const assert = require('assert'); -const simpleDispatcher = require('../helpers/simpleDispatcher'); +const simpleDispatcher = require('../helpers/simple-dispatcher'); describe('#admins', function verifySuite() { const constants = require('../../src/constants'); diff --git a/test/suites/getMetadata.js b/test/suites/get-metadata.js similarity index 100% rename from test/suites/getMetadata.js rename to test/suites/get-metadata.js diff --git a/test/suites/invite.js b/test/suites/invite.js index 5aab2ca31..f20e85f2a 100644 --- a/test/suites/invite.js +++ b/test/suites/invite.js @@ -83,7 +83,7 @@ describe('#invite', function registerSuite() { .reflect() .then(inspectPromise(false)) .then((err) => { - assert.equal(err.name, 'AssertionError [ERR_ASSERTION]'); + assert.equal(err.name, 'AssertionError'); assert.equal(err.message, `Sanity check failed for "id" failed: "abnormal@yandex.ru" vs "${email}"`); }); }); diff --git a/test/suites/organization/create.js b/test/suites/organization/create.js index 022083f80..55ff1917b 100644 --- a/test/suites/organization/create.js +++ b/test/suites/organization/create.js @@ -5,7 +5,7 @@ const sinon = require('sinon'); const faker = require('faker'); const { createMembers, createOrganization } = require('../../helpers/organization'); const scrypt = require('../../../src/utils/scrypt'); -const registerOrganizationMembers = require('../../../src/utils/organization/registerOrganizationMembers'); +const registerOrganizationMembers = require('../../../src/utils/organization/register-organization-members'); describe('#create organization', function registerSuite() { this.timeout(50000); diff --git a/test/suites/organization/getMetadata.js b/test/suites/organization/get-metadata.js similarity index 100% rename from test/suites/organization/getMetadata.js rename to test/suites/organization/get-metadata.js diff --git a/test/suites/organization/updateMetadata.js b/test/suites/organization/update-metadata.js similarity index 100% rename from test/suites/organization/updateMetadata.js rename to test/suites/organization/update-metadata.js diff --git a/test/suites/remove.js b/test/suites/remove.js index c516ccc96..6515192b0 100644 --- a/test/suites/remove.js +++ b/test/suites/remove.js @@ -2,7 +2,7 @@ const assert = require('assert'); const { inspectPromise } = require('@makeomatic/deploy'); const { USERS_ADMIN_ROLE } = require('../../src/constants'); -const simpleDispatcher = require('./../helpers/simpleDispatcher'); +const simpleDispatcher = require('./../helpers/simple-dispatcher'); describe('#remove', function registerSuite() { beforeEach(global.startService); diff --git a/test/suites/requestPassword.js b/test/suites/request-password.js similarity index 100% rename from test/suites/requestPassword.js rename to test/suites/request-password.js diff --git a/test/suites/updateMetadata.js b/test/suites/update-metadata.js similarity index 98% rename from test/suites/updateMetadata.js rename to test/suites/update-metadata.js index ef0a67265..b03ba7492 100644 --- a/test/suites/updateMetadata.js +++ b/test/suites/update-metadata.js @@ -1,6 +1,6 @@ const { inspectPromise } = require('@makeomatic/deploy'); const { expect } = require('chai'); -const simpleDispatcher = require('./../helpers/simpleDispatcher'); +const simpleDispatcher = require('./../helpers/simple-dispatcher'); describe('#updateMetadata', function getMetadataSuite() { const username = 'v@makeomatic.ru'; diff --git a/test/suites/updatePassword.js b/test/suites/update-password.js similarity index 98% rename from test/suites/updatePassword.js rename to test/suites/update-password.js index 81b37d6c2..bdf162024 100644 --- a/test/suites/updatePassword.js +++ b/test/suites/update-password.js @@ -1,7 +1,7 @@ const { inspectPromise } = require('@makeomatic/deploy'); const { expect } = require('chai'); const redisKey = require('../../src/utils/key.js'); -const simpleDispatcher = require('./../helpers/simpleDispatcher'); +const simpleDispatcher = require('./../helpers/simple-dispatcher'); describe('#updatePassword', function updatePasswordSuite() { const challenge = require('../../src/utils/challenges/challenge.js'); diff --git a/test/suites/verify.js b/test/suites/verify.js index 9b04b2fe8..49b5b52f4 100644 --- a/test/suites/verify.js +++ b/test/suites/verify.js @@ -1,7 +1,7 @@ const assert = require('assert'); const { inspectPromise } = require('@makeomatic/deploy'); const { expect } = require('chai'); -const simpleDispatcher = require('./../helpers/simpleDispatcher'); +const simpleDispatcher = require('./../helpers/simple-dispatcher'); describe('#verify', function verifySuite() { beforeEach(global.startService); diff --git a/yarn.lock b/yarn.lock index ef3f50ad5..882c5a4bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@babel/cli@^7.6.2": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.2.tgz#4ce8b5b4b2e4b4c1b7bd841cec62085e2dfc4465" - integrity sha512-JDZ+T/br9pPfT2lmAMJypJDTTTHM9ePD/ED10TRjRzJVdEVy+JB3iRlhzYmTt5YkNgHvxWGlUVnLtdv6ruiDrQ== +"@babel/cli@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.4.tgz#9b35a4e15fa7d8f487418aaa8229c8b0bc815f20" + integrity sha512-tqrDyvPryBM6xjIyKKUwr3s8CzmmYidwgdswd7Uc/Cv0ogZcuS1TYQTLx/eWKP3UbJ6JxZAiYlBZabXm/rtRsQ== dependencies: commander "^2.8.1" convert-source-map "^1.1.0" @@ -26,18 +26,18 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@^7.6.2": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.2.tgz#069a776e8d5e9eefff76236bc8845566bd31dd91" - integrity sha512-l8zto/fuoZIbncm+01p8zPSDZu/VuuJhAfA7d/AbzM09WR7iVhavvfNDYCNpo1VvLk6E6xgAoP9P+/EMJHuRkQ== +"@babel/core@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.4.tgz#6ebd9fe00925f6c3e177bb726a188b5f578088ff" + integrity sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ== dependencies: "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.6.2" + "@babel/generator" "^7.6.4" "@babel/helpers" "^7.6.2" - "@babel/parser" "^7.6.2" + "@babel/parser" "^7.6.4" "@babel/template" "^7.6.0" - "@babel/traverse" "^7.6.2" - "@babel/types" "^7.6.0" + "@babel/traverse" "^7.6.3" + "@babel/types" "^7.6.3" convert-source-map "^1.1.0" debug "^4.1.0" json5 "^2.1.0" @@ -46,23 +46,22 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.4.0", "@babel/generator@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.0.tgz#e2c21efbfd3293ad819a2359b448f002bfdfda56" - integrity sha512-Ms8Mo7YBdMMn1BYuNtKuP/z0TgEIhbcyB8HVR6PPNYp4P61lMsABiS4A3VG1qznjXVCf3r+fVHhm4efTYVsySA== +"@babel/generator@^7.4.0", "@babel/generator@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.3.tgz#71d5375264f93ec7bac7d9f35a67067733f5578e" + integrity sha512-hLhYbAb3pHwxjlijC4AQ7mqZdcoujiNaW7izCT04CIowHK8psN0IN8QjDv0iyFtycF5FowUOTwDloIheI25aMw== dependencies: - "@babel/types" "^7.6.0" + "@babel/types" "^7.6.3" jsesc "^2.5.1" lodash "^4.17.13" - source-map "^0.5.0" - trim-right "^1.0.1" + source-map "^0.6.1" -"@babel/generator@^7.6.2": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.2.tgz#dac8a3c2df118334c2a29ff3446da1636a8f8c03" - integrity sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ== +"@babel/generator@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" + integrity sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w== dependencies: - "@babel/types" "^7.6.0" + "@babel/types" "^7.6.3" jsesc "^2.5.1" lodash "^4.17.13" source-map "^0.5.0" @@ -149,20 +148,15 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.4.3": - version "7.5.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.5.5.tgz#02f077ac8817d3df4a832ef59de67565e71cca4b" - integrity sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g== +"@babel/parser@^7.0.0", "@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.3.tgz#9eff8b9c3eeae16a74d8d4ff30da2bd0d6f0487e" + integrity sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg== -"@babel/parser@^7.6.0": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.0.tgz#3e05d0647432a8326cb28d0de03895ae5a57f39b" - integrity sha512-+o2q111WEx4srBs7L9eJmcwi655eD8sXniLqMB93TBK9GrNzGrxDWSjiqz2hLU0Ha8MTXFIP0yd9fNdP+m43ZQ== - -"@babel/parser@^7.6.2": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.2.tgz#205e9c95e16ba3b8b96090677a67c9d6075b70a1" - integrity sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg== +"@babel/parser@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" + integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A== "@babel/plugin-proposal-class-properties@^7.5.5": version "7.5.5" @@ -214,141 +208,126 @@ "@babel/parser" "^7.6.0" "@babel/types" "^7.6.0" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.5.5": - version "7.6.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.0.tgz#389391d510f79be7ce2ddd6717be66d3fed4b516" - integrity sha512-93t52SaOBgml/xY74lsmt7xOR4ufYvhb5c5qiM6lu4J/dWGMAfAh6eKw4PjLes6DI6nQgearoxnFJk60YchpvQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.4.3", "@babel/traverse@^7.5.5", "@babel/traverse@^7.6.2", "@babel/traverse@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.3.tgz#66d7dba146b086703c0fb10dd588b7364cec47f9" + integrity sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw== dependencies: "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.6.0" + "@babel/generator" "^7.6.3" "@babel/helper-function-name" "^7.1.0" "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.6.0" - "@babel/types" "^7.6.0" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.13" - -"@babel/traverse@^7.6.2": - version "7.6.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.2.tgz#b0e2bfd401d339ce0e6c05690206d1e11502ce2c" - integrity sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ== - dependencies: - "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.6.2" - "@babel/helper-function-name" "^7.1.0" - "@babel/helper-split-export-declaration" "^7.4.4" - "@babel/parser" "^7.6.2" - "@babel/types" "^7.6.0" + "@babel/parser" "^7.6.3" + "@babel/types" "^7.6.3" debug "^4.1.0" globals "^11.1.0" lodash "^4.17.13" -"@babel/types@^7.0.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0": - version "7.6.1" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.1.tgz#53abf3308add3ac2a2884d539151c57c4b3ac648" - integrity sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g== +"@babel/types@^7.0.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.5.5", "@babel/types@^7.6.0", "@babel/types@^7.6.3": + version "7.6.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.3.tgz#3f07d96f854f98e2fbd45c64b0cb942d11e8ba09" + integrity sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA== dependencies: esutils "^2.0.2" lodash "^4.17.13" to-fast-properties "^2.0.0" -"@commitlint/cli@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-8.1.0.tgz#a3d4236c0ac961d7026a53d728b179c696d6a045" - integrity sha512-83K5C2nIAgoZlzMegf0/MEBjX+ampUyc/u79RxgX9ZYjzos+RQtNyO7I43dztVxPXSwAnX9XRgoOfkGWA4nbig== +"@commitlint/cli@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-8.2.0.tgz#fbf9969e04e2162d985eaa644fdad6ce807aadb6" + integrity sha512-8fJ5pmytc38yw2QWbTTJmXLfSiWPwMkHH4govo9zJ/+ERPBF2jvlxD/dQvk24ezcizjKc6LFka2edYC4OQ+Dgw== dependencies: - "@commitlint/format" "^8.1.0" - "@commitlint/lint" "^8.1.0" - "@commitlint/load" "^8.1.0" - "@commitlint/read" "^8.1.0" + "@commitlint/format" "^8.2.0" + "@commitlint/lint" "^8.2.0" + "@commitlint/load" "^8.2.0" + "@commitlint/read" "^8.2.0" babel-polyfill "6.26.0" - chalk "2.3.1" + chalk "2.4.2" get-stdin "7.0.0" lodash "4.17.14" meow "5.0.0" resolve-from "5.0.0" resolve-global "1.0.0" -"@commitlint/ensure@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-8.1.0.tgz#6c669f85c3005ed15c8141d83cf5312c43001613" - integrity sha512-dBU4CcjN0vJSDNOeSpaHNgQ1ra444u4USvI6PTaHVAS4aeDpZ5Cds1rxkZNsocu48WNycUu0jP84+zjcw2pPLQ== +"@commitlint/ensure@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-8.2.0.tgz#fad0c81c3d3bd09aa5fbcbcc483ae1f39bc8af8f" + integrity sha512-XZZih/kcRrqK7lEORbSYCfqQw6byfsFbLygRGVdJMlCPGu9E2MjpwCtoj5z7y/lKfUB3MJaBhzn2muJqS1gC6A== dependencies: lodash "4.17.14" -"@commitlint/execute-rule@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-8.1.0.tgz#e8386bd0836b3dcdd41ebb9d5904bbeb447e4715" - integrity sha512-+vpH3RFuO6ypuCqhP2rSqTjFTQ7ClzXtUvXphpROv9v9+7zH4L+Ex+wZLVkL8Xj2cxefSLn/5Kcqa9XyJTn3kg== +"@commitlint/execute-rule@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-8.2.0.tgz#aefb3744e22613660adefb7ebcccaa60bd24e78d" + integrity sha512-9MBRthHaulbWTa8ReG2Oii2qc117NuvzhZdnkuKuYLhker7sUXGFcVhLanuWUKGyfyI2o9zVr/NHsNbCCsTzAA== -"@commitlint/format@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-8.1.0.tgz#c3f3ca78bb74cbc1cce1368c0974b0cb8f31b98e" - integrity sha512-D0cmabUTQIKdABgt08d9JAvO9+lMRAmkcsZx8TMScY502R67HCw77JhzRDcw1RmqX5rN8JO6ZjDHO92Pbwlt+Q== +"@commitlint/format@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-8.2.0.tgz#0a2447fadac7c0421ce8a8d7e27dfa2172c737d4" + integrity sha512-sA77agkDEMsEMrlGhrLtAg8vRexkOofEEv/CZX+4xlANyAz2kNwJvMg33lcL65CBhqKEnRRJRxfZ1ZqcujdKcQ== dependencies: chalk "^2.0.1" -"@commitlint/is-ignored@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-8.1.0.tgz#c0583fa3c641b2d4898be1443e70e9c467429de2" - integrity sha512-HUSxx6kuLbqrQ8jb5QRzo+yR+CIXgA9HNcIcZ1qWrb+O9GOixt3mlW8li1IcfIgfODlaWoxIz0jYCxR08IoQLg== +"@commitlint/is-ignored@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-8.2.0.tgz#b6409ab28bf5a80f25e14da17da3916adb230a89" + integrity sha512-ADaGnKfbfV6KD1pETp0Qf7XAyc75xTy3WJlbvPbwZ4oPdBMsXF0oXEEGMis6qABfU2IXan5/KAJgAFX3vdd0jA== dependencies: "@types/semver" "^6.0.1" - semver "6.1.1" + semver "6.2.0" -"@commitlint/lint@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-8.1.0.tgz#ad10f4885c06f14c71de11dcd6bf2ca54a395141" - integrity sha512-WYjbUgtqvnlVH3S3XPZMAa+N7KO0yQ+GuUG20Qra+EtER6SRYawykmEs4wAyrmY8VcFXUnKgSlIQUsqmGKwNZQ== +"@commitlint/lint@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-8.2.0.tgz#aadc606379f3550eb877f16d4f5b103639cbf92a" + integrity sha512-ch9JN8aR37ufdjoWv50jLfvFz9rWMgLW5HEkMGLsM/51gjekmQYS5NJg8S2+6F5+jmralAO7VkUMI6FukXKX0A== dependencies: - "@commitlint/is-ignored" "^8.1.0" - "@commitlint/parse" "^8.1.0" - "@commitlint/rules" "^8.1.0" + "@commitlint/is-ignored" "^8.2.0" + "@commitlint/parse" "^8.2.0" + "@commitlint/rules" "^8.2.0" babel-runtime "^6.23.0" lodash "4.17.14" -"@commitlint/load@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-8.1.0.tgz#63b72ae5bb9152b8fa5b17c5428053032a9a49c8" - integrity sha512-ra02Dvmd7Gp1+uFLzTY3yGOpHjPzl5T9wYg/xrtPJNiOWXvQ0Mw7THw+ucd1M5iLUWjvdavv2N87YDRc428wHg== +"@commitlint/load@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-8.2.0.tgz#9ca53a0c795e4f63d796b4d42279e856549add1a" + integrity sha512-EV6PfAY/p83QynNd1llHxJiNxKmp43g8+7dZbyfHFbsGOdokrCnoelAVZ+WGgktXwLN/uXyfkcIAxwac015UYw== dependencies: - "@commitlint/execute-rule" "^8.1.0" - "@commitlint/resolve-extends" "^8.1.0" + "@commitlint/execute-rule" "^8.2.0" + "@commitlint/resolve-extends" "^8.2.0" babel-runtime "^6.23.0" chalk "2.4.2" cosmiconfig "^5.2.0" lodash "4.17.14" resolve-from "^5.0.0" -"@commitlint/message@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-8.1.0.tgz#8fb8046ddaa7e5c846a79da7cdbd15cf1a7770ae" - integrity sha512-AjHq022G8jQQ/3YrBOjwVBD4xF75hvC3vcvFoBIb7cC8vad1QWq+1w+aks0KlEK5IW+/+7ORZXIH+oyW7h3+8A== +"@commitlint/message@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-8.2.0.tgz#bdc0388183f6bc6006c7e7e197a721683011907a" + integrity sha512-LNsSwDLIFgE3nb/Sb1PIluYNy4Q8igdf4tpJCdv5JJDf7CZCZt3ZTglj0YutZZorpRRuHJsVIB2+dI4bVH3bFw== -"@commitlint/parse@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-8.1.0.tgz#833243c6d848e7a7e775a283b38697166ed2fd22" - integrity sha512-n4fEbZ5kdK5HChvne7Mj8rGGkKMfA4H11IuWiWmmMzgmZTNb/B04LPrzdUm4lm3f10XzM2JMM7PLXqofQJOGvA== +"@commitlint/parse@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-8.2.0.tgz#de80137e89ee5a2d3029656c9b33e90c88c6f56c" + integrity sha512-vzouqroTXG6QXApkrps0gbeSYW6w5drpUk7QAeZIcaCSPsQXDM8eqqt98ZzlzLJHo5oPNXPX1AAVSTrssvHemA== dependencies: conventional-changelog-angular "^1.3.3" conventional-commits-parser "^2.1.0" lodash "^4.17.11" -"@commitlint/read@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-8.1.0.tgz#effe07c965ba1735a5f7f8b7b19ac4d98c887507" - integrity sha512-PKsGMQFEr2sX/+orI71b82iyi8xFqb7F4cTvsLxzB5x6/QutxPVM3rg+tEVdi6rBKIDuqRIp2puDZQuREZs3vg== +"@commitlint/read@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-8.2.0.tgz#54c6549723d532c74434ee0d74e0459032dc9159" + integrity sha512-1tBai1VuSQmsOTsvJr3Fi/GZqX3zdxRqYe/yN4i3cLA5S2Y4QGJ5I3l6nGZlKgm/sSelTCVKHltrfWU8s5H7SA== dependencies: - "@commitlint/top-level" "^8.1.0" + "@commitlint/top-level" "^8.2.0" "@marionebl/sander" "^0.6.0" babel-runtime "^6.23.0" git-raw-commits "^1.3.0" -"@commitlint/resolve-extends@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-8.1.0.tgz#ed67f2ee484160ac8e0078bae52f172625157472" - integrity sha512-r/y+CeKW72Oa9BUctS1+I/MFCDiI3lfhwfQ65Tpfn6eZ4CuBYKzrCRi++GTHeAFKE3y8q1epJq5Rl/1GBejtBw== +"@commitlint/resolve-extends@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-8.2.0.tgz#b7f2f0c71c10f24b98a199ed11d2c14cfd7a318f" + integrity sha512-cwi0HUsDcD502HBP8huXfTkVuWmeo1Fiz3GKxNwMBBsJV4+bKa7QrtxbNpXhVuarX7QjWfNTvmW6KmFS7YK9uw== dependencies: "@types/node" "^12.0.2" import-fresh "^3.0.0" @@ -356,25 +335,25 @@ resolve-from "^5.0.0" resolve-global "^1.0.0" -"@commitlint/rules@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-8.1.0.tgz#009c64a8a23feb4647e5a25057997be62a272c8a" - integrity sha512-hlM8VfNjsOkbvMteFyqn0c3akiUjqG09Iid28MBLrXl/d+8BR3eTzwJ4wMta4oz/iqGyrIywvg1FpHrV977MPA== +"@commitlint/rules@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-8.2.0.tgz#4cd6a323ca1a3f3d33ae6dc723f8c88f3dcde347" + integrity sha512-FlqSBBP2Gxt5Ibw+bxdYpzqYR6HI8NIBpaTBhAjSEAduQtdWFMOhF0zsgkwH7lHN7opaLcnY2fXxAhbzTmJQQA== dependencies: - "@commitlint/ensure" "^8.1.0" - "@commitlint/message" "^8.1.0" - "@commitlint/to-lines" "^8.1.0" + "@commitlint/ensure" "^8.2.0" + "@commitlint/message" "^8.2.0" + "@commitlint/to-lines" "^8.2.0" babel-runtime "^6.23.0" -"@commitlint/to-lines@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-8.1.0.tgz#5bf2597f46acacec4b1b3dba832ac8934798b22a" - integrity sha512-Lh4OH1bInI8GME/7FggS0/XkIMEJdTObMbXRyPRGaPcWH5S7zpB6y+b4qjzBHXAbEv2O46QAAMjZ+ywPQCpmYQ== +"@commitlint/to-lines@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-8.2.0.tgz#dddb5916a457e1a79e437115a9b8eac7bf9ad52a" + integrity sha512-LXTYG3sMenlN5qwyTZ6czOULVcx46uMy+MEVqpvCgptqr/MZcV/C2J+S2o1DGwj1gOEFMpqrZaE3/1R2Q+N8ng== -"@commitlint/top-level@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-8.1.0.tgz#f1950de73a1f76ef5c9e753a6b77402e0755d677" - integrity sha512-EvQuofuA/+0l1w9pkG/PRyIwACmZdIh9qxyax7w7mR8qqmSHscqf2jARIylh1TOx0uI9egO8MuPLiwC1RwyREA== +"@commitlint/top-level@^8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-8.2.0.tgz#206e7cbc54dbe9494190677f887dd60943fed5b0" + integrity sha512-Yaw4KmYNy31/HhRUuZ+fupFcDalnfpdu4JGBgGAqS9aBHdMSSWdWqtAaDaxdtWjTZeN3O0sA2gOhXwvKwiDwvw== dependencies: find-up "^4.0.0" @@ -386,12 +365,7 @@ "@hapi/boom" "7.x.x" "@hapi/hoek" "8.x.x" -"@hapi/address@2.x.x": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.1.tgz#61395b5ed94c4cb19c2dc4c85969cff3d40d583f" - integrity sha512-DYuHzu978pP1XW1GD3HGvLnAFjbQTIgc2+V153FGkbS2pgo9haigCdwBnUDrbhaOkgiJlbZvoEqDrcxSLHpiWA== - -"@hapi/address@^2.1.2": +"@hapi/address@2.x.x", "@hapi/address@^2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.2.tgz#1c794cd6dbf2354d1eb1ef10e0303f573e1c7222" integrity sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q== @@ -423,9 +397,9 @@ "@hapi/wreck" "15.x.x" "@hapi/boom@7.x.x": - version "7.4.3" - resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-7.4.3.tgz#79ffed6ef8c625046a3bd069abed5a9d35fc50c1" - integrity sha512-3di+R+BcGS7HKy67Zi6mIga8orf67GdR0ubDEVBG1oqz3y9B70LewsuCMCSvWWLKlI6V1+266zqhYzjMrPGvZw== + version "7.4.11" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-7.4.11.tgz#37af8417eb9416aef3367aa60fa04a1a9f1fc262" + integrity sha512-VSU/Cnj1DXouukYxxkes4nNJonCnlogHvIff1v1RVoN4xzkKhMXX+GRmb3NyH1iar10I9WFPDv2JPwfH3GaV0A== dependencies: "@hapi/hoek" "8.x.x" @@ -459,13 +433,13 @@ "@hapi/hoek" "8.x.x" "@hapi/catbox@10.x.x": - version "10.2.2" - resolved "https://registry.yarnpkg.com/@hapi/catbox/-/catbox-10.2.2.tgz#e5c7da6718f5ab5f3a0beefbf352983a3b988626" - integrity sha512-a4KejaKqDOMdwo/PIYoAaObVMmkfkG3RS85kPqNTTURjWnIV1+rrZ938f6RCz5EbrroKbuNC0bcvAt7lAD5LNg== + version "10.2.3" + resolved "https://registry.yarnpkg.com/@hapi/catbox/-/catbox-10.2.3.tgz#2df51ab943d7613df3718fa2bfd981dd9558cec5" + integrity sha512-kN9hXO4NYyOHW09CXiuj5qW1syc/0XeVOBsNNk0Tz89wWNQE5h21WF+VsfAw3uFR8swn/Wj3YEVBnWqo82m/JQ== dependencies: "@hapi/boom" "7.x.x" "@hapi/hoek" "8.x.x" - "@hapi/joi" "15.x.x" + "@hapi/joi" "16.x.x" "@hapi/podium" "3.x.x" "@hapi/content@4.x.x": @@ -476,9 +450,9 @@ "@hapi/boom" "7.x.x" "@hapi/cryptiles@4.x.x": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@hapi/cryptiles/-/cryptiles-4.2.0.tgz#d22bd5afa54f3c1a3b944a43dadfd4c5e5747f72" - integrity sha512-P+ioMP1JGhwDOKPRuQls6sT/ln6Fk+Ks6d90mlBi6HcOu5itvdUiFv5Ynq2DvLadPDWaA43lwNxkfZrjE9s2MA== + version "4.2.1" + resolved "https://registry.yarnpkg.com/@hapi/cryptiles/-/cryptiles-4.2.1.tgz#ff0f18d79074659838caedbb911851313ad1ffbc" + integrity sha512-XoqgKsHK0l/VpqPs+tr6j6vE+VQ3+2bkF2stvttmc7xAOf1oSAwHcJ0tlp/6MxMysktt1IEY0Csy3khKaP9/uQ== dependencies: "@hapi/boom" "7.x.x" @@ -517,13 +491,13 @@ "@hapi/topo" "3.x.x" "@hapi/heavy@6.x.x": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@hapi/heavy/-/heavy-6.2.1.tgz#4f10877707b6c8a6ad1e9dbb6a3d13c24b3a3a29" - integrity sha512-uaEyC4AtGCGKt/LLBbdDQxJP1bFAbxiot6n/fwa4kyo6w8ULpXXCh8FxLlJ5mC06lqbAxQv45JyozIB6P4Dsig== + version "6.2.2" + resolved "https://registry.yarnpkg.com/@hapi/heavy/-/heavy-6.2.2.tgz#d42a282c62d5bb6332e497d8ce9ba52f1609f3e6" + integrity sha512-PY1dCCO6dsze7RlafIRhTaGeyTgVe49A/lSkxbhKGjQ7x46o/OFf7hLiRqTCDh3atcEKf6362EaB3+kTUbCsVA== dependencies: "@hapi/boom" "7.x.x" "@hapi/hoek" "8.x.x" - "@hapi/joi" "15.x.x" + "@hapi/joi" "16.x.x" "@hapi/hoek@8.2.1": version "8.2.1" @@ -531,17 +505,18 @@ integrity sha512-JPiBy+oSmsq3St7XlipfN5pNA6bDJ1kpa73PrK/zR29CVClDVqy04AanM/M/qx5bSF+I61DdCfAvRrujau+zRg== "@hapi/hoek@8.x.x", "@hapi/hoek@^8.2.4": - version "8.2.4" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.2.4.tgz#684a14f4ca35d46f44abc87dfc696e5e4fe8a020" - integrity sha512-Ze5SDNt325yZvNO7s5C4fXDscjJ6dcqLFXJQ/M7dZRQCewuDj2iDUuBi6jLQt+APbW9RjjVEvLr35FXuOEqjow== + version "8.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.3.0.tgz#2b9db1cd00f3891005c77b3a8d608b88a6d0aa4d" + integrity sha512-C0QL9bmgUXTSuf8nDeGrpMjtJG7tPUr8wG6/wxPbP62tGwCwQtdMSJYfESowmY4P3Hn593f+8OzNY5bckcu/LQ== "@hapi/iron@5.x.x": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@hapi/iron/-/iron-5.1.1.tgz#0e819729f26ee76dea68e4c4e4b5c996fd0513a5" - integrity sha512-QYfm6nofZ19pIxm8LR0lsANBabrdxqe0vUYKKI+0w9VdCetoove+dxfbLfduVDM72kh/RNOQG6E5/xyI826PcA== + version "5.1.4" + resolved "https://registry.yarnpkg.com/@hapi/iron/-/iron-5.1.4.tgz#7406f36847f798f52b92d1d97f855e27973832b7" + integrity sha512-+ElC+OCiwWLjlJBmm8ZEWjlfzTMQTdgPnU/TsoU5QsktspIWmWi9IU4kU83nH+X/SSya8TP8h8P11Wr5L7dkQQ== dependencies: "@hapi/b64" "4.x.x" "@hapi/boom" "7.x.x" + "@hapi/bourne" "1.x.x" "@hapi/cryptiles" "4.x.x" "@hapi/hoek" "8.x.x" @@ -556,9 +531,9 @@ "@hapi/topo" "3.x.x" "@hapi/joi@16.x.x": - version "16.1.4" - resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.4.tgz#b039fe474a0ab838c1a90620c53a208fcef75d99" - integrity sha512-m7ctezhxjob+dSpXnCNlgAj6rrEpdSsaWu3GWL3g1AybQCU36mlAo9IwGFJwIxD+oHgdO6mYyviYlaejX+qN6g== + version "16.1.7" + resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.7.tgz#360857223a87bb1f5f67691537964c1b4908ed93" + integrity sha512-anaIgnZhNooG3LJLrTFzgGALTiO97zRA1UkvQHm9KxxoSiIzCozB3RCNCpDnfhTJD72QlrHA8nwGmNgpFFCIeg== dependencies: "@hapi/address" "^2.1.2" "@hapi/formula" "^1.2.0" @@ -599,20 +574,20 @@ integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ== "@hapi/podium@3.x.x": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@hapi/podium/-/podium-3.4.1.tgz#05ae4cba21ddc78bd4d026c720ed05d8b4e9474e" - integrity sha512-WbwYr5nK+GIrCdgEbN8R7Mh7z+j9AgntOLQ/YQdeLtBp+uScVmW9FoycKdNS5uweO74xwICr28Ob0DU74a2zmg== + version "3.4.2" + resolved "https://registry.yarnpkg.com/@hapi/podium/-/podium-3.4.2.tgz#3d3a2cb6aabbe46f98658151295d42030442bddc" + integrity sha512-g9zlAkRL2uDlnEo64xzEhFLblf4fdL5Z6evAO0wJhdxEvokI/+6ryv7k6uhND481LiLzQz8qTtPYMuhH1hichw== dependencies: "@hapi/hoek" "8.x.x" - "@hapi/joi" "15.x.x" + "@hapi/joi" "16.x.x" "@hapi/shot@4.x.x": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@hapi/shot/-/shot-4.1.1.tgz#7b5ca71f63a159a0b28a487cd293f35b04164439" - integrity sha512-TrsqCyaq24XcdvD0bSi26hjwyQQy5q/nzpasbPNgPLoGnxW3sCWE7ws3ba6dd6Atb8TEh9QBD7mBQDCrMMz2Ig== + version "4.1.2" + resolved "https://registry.yarnpkg.com/@hapi/shot/-/shot-4.1.2.tgz#69f999956041fe468701a89a413175a521dabed5" + integrity sha512-6LeHLjvsq/bQ0R+fhEyr7mqExRGguNTrxFZf5DyKe3CK6pNabiGgYO4JVFaRrLZ3JyuhkS0fo8iiRE2Ql2oA/A== dependencies: "@hapi/hoek" "8.x.x" - "@hapi/joi" "15.x.x" + "@hapi/joi" "16.x.x" "@hapi/somever@2.x.x": version "2.1.1" @@ -623,9 +598,9 @@ "@hapi/hoek" "8.x.x" "@hapi/statehood@6.x.x": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@hapi/statehood/-/statehood-6.1.1.tgz#64e9787163c57201c120b2381ba1982722ad0d8a" - integrity sha512-tMfS6B8QdrqTaKRUhHv6Ur7oPK6kcEZcnnvBK4IuaPZA9ma5UsyprTXkzbiB0V+0E56dMg3RabO1SABeZkzy6g== + version "6.1.2" + resolved "https://registry.yarnpkg.com/@hapi/statehood/-/statehood-6.1.2.tgz#6dda508b5da99a28a3ed295c3cac795cf6c12a02" + integrity sha512-pYXw1x6npz/UfmtcpUhuMvdK5kuOGTKcJNfLqdNptzietK2UZH5RzNJSlv5bDHeSmordFM3kGItcuQWX2lj2nQ== dependencies: "@hapi/boom" "7.x.x" "@hapi/bounce" "1.x.x" @@ -633,12 +608,12 @@ "@hapi/cryptiles" "4.x.x" "@hapi/hoek" "8.x.x" "@hapi/iron" "5.x.x" - "@hapi/joi" "15.x.x" + "@hapi/joi" "16.x.x" "@hapi/subtext@6.x.x": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@hapi/subtext/-/subtext-6.1.1.tgz#3b63b4175a7bd06cd0d21f13912c7d81de90e6d2" - integrity sha512-Y7NjKFRPwlzKRw5IdwRou42hR4IBQZolT+/DlvfSr/CBjGyu38n5+9LKfNKzqB/0AVEk+xynCijsx1o1UVWX8A== + version "6.1.2" + resolved "https://registry.yarnpkg.com/@hapi/subtext/-/subtext-6.1.2.tgz#87754f0c25b4e2676575a3686541e5b555b0c717" + integrity sha512-G1kqD1E2QdxpvpL26WieIyo3z0qCa/sAGSa2TJI/PYPWCR9rL0rqFvhWY774xPZ4uK1PV3TIaJcx8AruAvxclg== dependencies: "@hapi/boom" "7.x.x" "@hapi/bourne" "1.x.x" @@ -653,17 +628,10 @@ resolved "https://registry.yarnpkg.com/@hapi/teamwork/-/teamwork-3.3.1.tgz#b52d0ec48682dc793926bd432e22ceb19c915d3f" integrity sha512-61tiqWCYvMKP7fCTXy0M4VE6uNIwA0qvgFoiDubgfj7uqJ0fdHJFQNnVPGrxhLWlwz0uBPWrQlBH7r8y9vFITQ== -"@hapi/topo@3.x.x": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.3.tgz#c7a02e0d936596d29f184e6d7fdc07e8b5efce11" - integrity sha512-JmS9/vQK6dcUYn7wc2YZTqzIKubAQcJKu2KCKAru6es482U5RT5fP1EXCPtlXpiK7PR0On/kpQKI4fRKkzpZBQ== - dependencies: - "@hapi/hoek" "8.x.x" - -"@hapi/topo@^3.1.3": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.4.tgz#42e2fe36f593d90ad258a08b582be128c141c45d" - integrity sha512-aVWQTOI9wBD6zawmOr6f+tdEIxQC8JXfQVLTjgGe8YEStAWGn/GNNVTobKJhbWKveQj2RyYF3oYbO9SC8/eOCA== +"@hapi/topo@3.x.x", "@hapi/topo@^3.1.3": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.5.tgz#3baea17e456530edad69a75c3fc7cde97dd6d331" + integrity sha512-bi9m1jrui9LlvtVdLaHv0DqeOoe+I8dep+nEcTgW6XxJHL3xArQcilYz3tIp0cRC4gWlsVtABK7vNKg4jzEmAA== dependencies: "@hapi/hoek" "8.x.x" @@ -685,9 +653,9 @@ "@hapi/joi" "16.x.x" "@hapi/wreck@15.x.x": - version "15.0.2" - resolved "https://registry.yarnpkg.com/@hapi/wreck/-/wreck-15.0.2.tgz#e3e1b2830237792f8f6a372caee7866a77d8718d" - integrity sha512-D/7sGmx3XxxkaMWHZDKTMai8rIEfIgE+DnoZeKfmxhKGgvIpMu1f8BBmLADbdniccGer79w74IWWdXleNrT1Rw== + version "15.1.0" + resolved "https://registry.yarnpkg.com/@hapi/wreck/-/wreck-15.1.0.tgz#7917cd25950ce9b023f7fd2bea6e2ef72c71e59d" + integrity sha512-tQczYRTTeYBmvhsek/D49En/5khcShaBEmzrAaDjMrFXKJRuF8xA8+tlq1ETLBFwGd6Do6g2OC74rt11kzawzg== dependencies: "@hapi/boom" "7.x.x" "@hapi/bourne" "1.x.x" @@ -703,19 +671,19 @@ alce "1.x.x" yargs "14.0.0" -"@makeomatic/deploy@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@makeomatic/deploy/-/deploy-9.1.1.tgz#bc4f0d97c49567aa9ac09cd1aaa67a25617dfd57" - integrity sha512-gCR29hnEGVexyF7Q/EQFqTld6LQBwmoegFx3OLpye09hz+zD3E3BCk8ED4ruXCbVTELvEcK0BltaO5f+dxzYrw== +"@makeomatic/deploy@^9.3.2": + version "9.3.2" + resolved "https://registry.yarnpkg.com/@makeomatic/deploy/-/deploy-9.3.2.tgz#3c3a5b6a6006999b0daf8b49f1f62520996a7c08" + integrity sha512-9oNXSPCXOTAr6xtV4q15TnZPGEb943UmBvplc0xXh3NfqRanJKW/VU6BQ7SRsvB3omGVBR55DWVqAlhfA0CpEw== dependencies: - "@commitlint/cli" "^8.1.0" - bluebird "^3.5.5" + "@commitlint/cli" "^8.2.0" + bluebird "^3.7.1" chrome-launcher "^0.11.2" chrome-remote-interface "^0.28.0" death "^1.1.0" find-up "^4.1.0" glob "^7.1.4" - husky "^3.0.5" + husky "^3.0.9" hyperid "^2.0.1" is "^3.2.1" js-yaml "^3.13.1" @@ -724,12 +692,12 @@ lodash.set "^4.3.2" ms-conf "^5.0.2" npm-path "^2.0.4" - pino "^5.13.3" + pino "^5.13.5" read-pkg "^5.2.0" rimraf "^3.0.0" semantic-release "15.13.24" shelljs "^0.8.3" - yargs "^14.0.0" + yargs "^14.2.0" "@marionebl/sander@^0.6.0": version "0.6.1" @@ -750,12 +718,12 @@ debug "^3.1.0" lodash "^4.17.10" -"@microfleet/core@^15.0.1": - version "15.0.1" - resolved "https://registry.yarnpkg.com/@microfleet/core/-/core-15.0.1.tgz#41725ca84cef00d2ca8d9cd9de95ed2b43c8a894" - integrity sha512-mdzBxtj1Cy3h+Rvw/0qRH7HVVgh4rqokLvFMB+cBzwi6ToMmBoBS15oIq2EYcBNJB/y+JqShKw920dZRs0gkZQ== +"@microfleet/core@^15.3.1": + version "15.3.1" + resolved "https://registry.yarnpkg.com/@microfleet/core/-/core-15.3.1.tgz#6a973083ab35da74c43812b2fa049b348c0cc5b6" + integrity sha512-F7fU1bjcKagn6Se1IX80pf01XWmou0QbQrcIJJO9FDbXb0S+nDF7lrYPgTf8rmrd44m9MTjwQzSL0XNDlR0HvA== dependencies: - bluebird "^3.5.5" + bluebird "^3.7.1" bluebird-retry "^0.11.0" callsite "^1.0.0" chalk "^2.4.1" @@ -764,23 +732,23 @@ event-to-promise "^0.8.0" eventemitter3 "^4.0.0" get-value "^3.0.1" - glob "^7.1.4" + glob "^7.1.5" is "^3.3.0" lodash "^4.17.15" lodash.intersection "^4.4.0" lodash.partial "^4.2.1" lsmod "^1.0.0" opentracing "^0.14.4" - pino "^5.13.2" + pino "^5.13.5" pino-multi-stream "^4.2.0" - pino-pretty "^3.2.1" - read-pkg-up "^6.0.0" + pino-pretty "^3.2.2" + read-pkg-up "^7.0.0" rfdc "^1.1.4" semver "^6.3.0" - sonic-boom "^0.7.5" + sonic-boom "^0.7.6" sort-by "^1.1.1" stdout-stream "^1.4.1" - yargs-parser "^14.0.0" + yargs-parser "^15.0.0" "@microfleet/transport-amqp@^15.0.0": version "15.0.0" @@ -816,31 +784,31 @@ debug "^4.1.1" glob "^7.1.3" -"@nodelib/fs.scandir@2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.2.tgz#1f981cd5b83e85cfdeb386fc693d4baab392fa54" - integrity sha512-wrIBsjA5pl13f0RN4Zx4FNWmU71lv03meGKnqRUoCyan17s4V3WL92f3w3AIuWbNnpcrQyFBU5qMavJoB8d27w== +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== dependencies: - "@nodelib/fs.stat" "2.0.2" + "@nodelib/fs.stat" "2.0.3" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.2", "@nodelib/fs.stat@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.2.tgz#2762aea8fe78ea256860182dcb52d61ee4b8fda6" - integrity sha512-z8+wGWV2dgUhLqrtRYa03yDx4HWMvXKi1z8g3m2JyxAx8F7xk74asqPk5LAETjqDSGLFML/6CDl0+yFunSYicw== +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== -"@nodelib/fs.walk@^1.2.1": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.3.tgz#a555dc256acaf00c62b0db29529028dd4d4cb141" - integrity sha512-l6t8xEhfK9Sa4YO5mIRdau7XSOADfmh3jCr0evNHdY+HNkW6xuQhgMH7D73VV6WpZOagrW0UludvMTiifiwTfA== +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== dependencies: - "@nodelib/fs.scandir" "2.1.2" + "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" "@octokit/endpoint@^5.1.0": - version "5.3.5" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.3.5.tgz#2822c3b01107806dbdce3863b6205e3eff4289ed" - integrity sha512-f8KqzIrnzPLiezDsZZPB+K8v8YSv6aKFl7eOu59O46lmlW4HagWl1U6NWl6LmT8d1w7NsKBI3paVtzcnRGO1gw== + version "5.4.0" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-5.4.0.tgz#5b44b3e8e92d88765daf127e1501174ecbca7bce" + integrity sha512-DWTNgEKg5KXzvNjKTzcFTnkZiL7te6pQxxumvxPjyjDpcY5V3xzywnNu1WVqySY3Ct1flF/kAoyDdZos6acq3Q== dependencies: is-plain-object "^3.0.0" universal-user-agent "^4.0.0" @@ -867,9 +835,9 @@ universal-user-agent "^4.0.0" "@octokit/rest@^16.27.0": - version "16.28.9" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.28.9.tgz#ac8c5f3ff305e9e0a0989a5245e4286f057a95d7" - integrity sha512-IKGnX+Tvzt7XHhs8f4ajqxyJvYAMNX5nWfoJm4CQj8LZToMiaJgutf5KxxpxoC3y5w7JTJpW5rnWnF4TsIvCLA== + version "16.33.0" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.33.0.tgz#13c1404b24c9871419eb31029de177e82f3eb851" + integrity sha512-t4jMR+odsfooQwmHiREoTQixVTX2DfdbSaO+lKrW9R5XBuk0DW+5T/JdfwtxAGUAHgvDDpWY/NVVDfEPTzxD6g== dependencies: "@octokit/request" "^5.0.0" "@octokit/request-error" "^1.0.2" @@ -884,10 +852,10 @@ once "^1.4.0" universal-user-agent "^4.0.0" -"@semantic-release/changelog@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@semantic-release/changelog/-/changelog-3.0.4.tgz#8fc578b76bca5b23c3000694277c38e0fa835edd" - integrity sha512-UqEPahcZSW0IKtzOglyjeEZCN99ku6Wb/yH/iOKEBJ7Vkw0/+Fc3VRiGoXTkMfHSFUJk+4UkoQKTlYuwf61C2w== +"@semantic-release/changelog@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@semantic-release/changelog/-/changelog-3.0.5.tgz#37423eee40f550acb8e09ff1d951ba79263e3b2d" + integrity sha512-/U44eK5qL2olevbEi+GrJxq1lNGUABChqK58A3SkiDsZS6AoGO8CJHQ7OG0zx+spxwkY4TevZ85Whz/hYyO+5w== dependencies: "@semantic-release/error" "^2.1.0" aggregate-error "^3.0.0" @@ -911,28 +879,28 @@ resolved "https://registry.yarnpkg.com/@semantic-release/error/-/error-2.2.0.tgz#ee9d5a09c9969eade1ec864776aeda5c5cddbbf0" integrity sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg== -"@semantic-release/exec@^3.3.7": - version "3.3.7" - resolved "https://registry.yarnpkg.com/@semantic-release/exec/-/exec-3.3.7.tgz#31415bed49bc885bbeffd6a277a431f46c4fa1e6" - integrity sha512-X7PKvhUi9PwnwKpCJSIe+liSEXLudc1cVXEvXHFTOVEbFyokd6G8y1+6hr3NqgMfEBfiH5uilySvszQIud5nZw== +"@semantic-release/exec@^3.3.8": + version "3.3.8" + resolved "https://registry.yarnpkg.com/@semantic-release/exec/-/exec-3.3.8.tgz#4e759818c47bdb36e0199d8fad0f5343421f6d8f" + integrity sha512-GH1v5BwXRIUAnvrXjil+R+9DjI+ELgk2NMdQUAnp2/qZ6YItZt6KI8HrY3zAFDrG0YGaOwC9XxuUNKeldsOK7A== dependencies: "@semantic-release/error" "^2.1.0" aggregate-error "^3.0.0" debug "^4.0.0" - execa "^1.0.0" + execa "^3.2.0" lodash "^4.17.4" parse-json "^5.0.0" -"@semantic-release/git@^7.0.16": - version "7.0.16" - resolved "https://registry.yarnpkg.com/@semantic-release/git/-/git-7.0.16.tgz#19921d2fdc75d712ae1706330dba8ebec8ced52d" - integrity sha512-Bw/npxTVTeFPnQZmuczWRGRdxqJpWOOFZENx38ykyp42InwDFm4n72bfcCwmP/J4WqkPmMR4p+IracWruz/RUw== +"@semantic-release/git@^7.0.17": + version "7.0.17" + resolved "https://registry.yarnpkg.com/@semantic-release/git/-/git-7.0.17.tgz#b58abf40a58d213fcfb2710c112dbc314b63c0f5" + integrity sha512-xtNXhdWW92VCG4EumZ/Ijj44Yezaau9pQ39KB4q3l17vyu55MwPzQjlhSfysPshwosmVkzp6LhvYIg9+xUL07A== dependencies: "@semantic-release/error" "^2.1.0" aggregate-error "^3.0.0" debug "^4.0.0" dir-glob "^3.0.0" - execa "^1.0.0" + execa "^3.2.0" fs-extra "^8.0.0" globby "^10.0.0" lodash "^4.17.4" @@ -940,9 +908,9 @@ p-reduce "^2.0.0" "@semantic-release/github@^5.1.0": - version "5.4.3" - resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-5.4.3.tgz#7c7fe716d0fa757d2d4a4075e5e7e1ff5d0f8942" - integrity sha512-nFoG1whDZettsGsMRE64kCFRpGSHxQxiKtUltKw67uYO7Q62049HGcdH7pZh/ipn+Uq2cG4Zef/g1vxVVaK82w== + version "5.5.3" + resolved "https://registry.yarnpkg.com/@semantic-release/github/-/github-5.5.3.tgz#280f5c3d1cbcd469005b0858867d8ceb41c7bc8f" + integrity sha512-7Fl6LRYA20Wn440gPWB+uZ3A1slokKS47Kd4uxQnXt4t4lXYTMrg1tpzkY1FUQgsWqq8PFgoJ3lCPoo8JOQwKg== dependencies: "@octokit/rest" "^16.27.0" "@semantic-release/error" "^2.2.0" @@ -953,8 +921,8 @@ fs-extra "^8.0.0" globby "^10.0.0" http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - issue-parser "^4.0.0" + https-proxy-agent "^3.0.0" + issue-parser "^5.0.0" lodash "^4.17.4" mime "^2.4.3" p-filter "^2.0.0" @@ -963,9 +931,9 @@ url-join "^4.0.0" "@semantic-release/npm@^5.0.5": - version "5.1.15" - resolved "https://registry.yarnpkg.com/@semantic-release/npm/-/npm-5.1.15.tgz#dbf5e37bc4ce4be0a48a82c27ca4ad1e584f12ee" - integrity sha512-MUUKOOtqsX/aJZJIjiAdw7SkCH+D3De060l1HhTlqrwTB7PzMtXcUMen6Prd1Hv8+gknUFkSWhVmi8tIaGDVnA== + version "5.2.0" + resolved "https://registry.yarnpkg.com/@semantic-release/npm/-/npm-5.2.0.tgz#e5643c43ca5f3eb7db3d403fe12e44c187c5f9c6" + integrity sha512-+WUoFNTVn4saPLbIBd+fCXL9nP6c49iOyvjLqWNq4lh2/sLZg993MMTBVvTxHRaojxb42R2RjePsO5AaFM3Lzw== dependencies: "@semantic-release/error" "^2.2.0" aggregate-error "^3.0.0" @@ -1003,9 +971,9 @@ type-detect "4.0.8" "@sinonjs/formatio@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.1.tgz#52310f2f9bcbc67bdac18c94ad4901b95fde267e" - integrity sha512-tsHvOB24rvyvV2+zKMmPkZ7dXX6LSLKZ7aOtXY6Edklp0uRcgGpOsQTTGTcWViFyx4uhWc6GV8QdnALbIbIdeQ== + version "3.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" + integrity sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ== dependencies: "@sinonjs/commons" "^1" "@sinonjs/samsam" "^3.1.0" @@ -1044,9 +1012,9 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*", "@types/node@^12.0.2": - version "12.7.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.4.tgz#64db61e0359eb5a8d99b55e05c729f130a678b04" - integrity sha512-W0+n1Y+gK/8G2P/piTkBBN38Qc5Q1ZSO6B5H3QmPCUewaiXOo2GCAWZ4ElZCcNhjJuBSUSLGFUJnmlCn5+nxOQ== + version "12.7.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.12.tgz#7c6c571cc2f3f3ac4a59a5f2bd48f5bdbc8653cc" + integrity sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -1081,10 +1049,15 @@ acorn-jsx@^5.0.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== -acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== +acorn-jsx@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== + +acorn@^7.0.0, acorn@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== agent-base@4, agent-base@^4.3.0: version "4.3.0" @@ -1108,12 +1081,12 @@ agentkeepalive@^3.4.1: humanize-ms "^1.2.1" aggregate-error@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.0.tgz#5b5a3c95e9095f311c9ab16c19fb4f3527cd3f79" - integrity sha512-yKD9kEoJIR+2IFqhMwayIBgheLYbB3PS2OBhWae1L/ODTd/JF/30cW0bc9TqzRL3k4U41Dieu3BF4I29p8xesA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" + integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== dependencies: clean-stack "^2.0.0" - indent-string "^3.2.0" + indent-string "^4.0.0" ajv-keywords@^3.2.0: version "3.4.1" @@ -1155,6 +1128,13 @@ ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-escapes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228" + integrity sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q== + dependencies: + type-fest "^0.5.2" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -1528,15 +1508,15 @@ bluebird-retry@^0.11.0: integrity sha1-EomrIsu8OgJYe6rTVZU1HdDBwEc= bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: - version "3.5.5" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" - integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== - -bluebird@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.0.tgz#56a6a886e03f6ae577cffedeb524f8f2450293cf" integrity sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg== +bluebird@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" + integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg== + boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -1797,15 +1777,6 @@ chai@^4.2.0: pathval "^1.1.0" type-detect "^4.0.5" -chalk@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796" - integrity sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g== - dependencies: - ansi-styles "^3.2.0" - escape-string-regexp "^1.0.5" - supports-color "^5.2.0" - chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1816,9 +1787,9 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4. supports-color "^5.3.0" chance@^1.0.18: - version "1.1.0" - resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.0.tgz#4206b8ae8679275afd7db6379abc23f103ab8488" - integrity sha512-DwT4HVvl1VwZ73lqEyWYtrI59iSERBDfioMhw0gMMNySLMcx7/pzy+JAam3+3WcEH4ITNzwRtuQH4Geyko70gw== + version "1.1.3" + resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.3.tgz#414f08634ee479c7a316b569050ea20751b82dd3" + integrity sha512-XeJsdoVAzDb1WRPRuMBesRSiWpW1uNTo5Fd7mYxPJsAfgX71+jfuCOHOdbyBz2uAUZ8TwKcXgWk3DMedFfJkbg== chardet@^0.7.0: version "0.7.0" @@ -1867,9 +1838,9 @@ chokidar@^2.1.8: fsevents "^1.2.7" chownr@^1.1.1, chownr@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" - integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== chrome-launcher@^0.11.2: version "0.11.2" @@ -1917,6 +1888,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" + integrity sha1-jffHquUf02h06PjQW5GAvBGj/tc= + dependencies: + escape-string-regexp "^1.0.5" + clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -1942,6 +1920,13 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + cli-table3@^0.5.0, cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -2077,9 +2062,9 @@ colors@1.0.3: integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= colors@^1.1.2, colors@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" - integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== colorspace@1.1.x: version "1.1.2" @@ -2109,11 +2094,16 @@ commander@2.11.x: resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== -commander@^2.19.0, commander@^2.8.1, commander@~2.20.0: +commander@2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== +commander@^2.19.0, commander@^2.8.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.1.tgz#3863ce3ca92d0831dcf2a102f5fb4b5926afd0f9" + integrity sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg== + common-errors@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/common-errors/-/common-errors-1.0.5.tgz#13f8aa9727ca71595d0067895e98ef4ba7fc994e" @@ -2173,9 +2163,9 @@ configstore@^3.0.0: xdg-basedir "^3.0.0" confusing-browser-globals@^1.0.7: - version "1.0.8" - resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.8.tgz#93ffec1f82a6e2bf2bc36769cc3a92fa20e502f3" - integrity sha512-lI7asCibVJ6Qd3FGU7mu4sfG4try4LX3+GVS+Gv8UlrEf2AeW57piecapnog2UHZSbcX/P/1UDWVaTsblowlZg== + version "1.0.9" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz#72bc13b483c0276801681871d4898516f8f54fdd" + integrity sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw== console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" @@ -2196,22 +2186,22 @@ conventional-changelog-angular@^1.3.3: q "^1.5.1" conventional-changelog-angular@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.3.tgz#299fdd43df5a1f095283ac16aeedfb0a682ecab0" - integrity sha512-YD1xzH7r9yXQte/HF9JBuEDfvjxxwDGGwZU1+ndanbY0oFgA+Po1T9JDSpPLdP0pZT6MhCAsdvFKC4TJ4MTJTA== + version "5.0.5" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.5.tgz#69b541bcf3e538a8578b1e5fbaabe9bd8f572b57" + integrity sha512-RrkdWnL/TVyWV1ayWmSsrWorsTDqjL/VwG5ZSEneBQrd65ONcfeA1cW7FLtNweQyMiKOyriCMTKRSlk18DjTrw== dependencies: compare-func "^1.3.1" q "^1.5.1" conventional-changelog-writer@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.7.tgz#e4b7d9cbea902394ad671f67108a71fa90c7095f" - integrity sha512-p/wzs9eYaxhFbrmX/mCJNwJuvvHR+j4Fd0SQa2xyAhYed6KBiZ780LvoqUUvsayP4R1DtC27czalGUhKV2oabw== + version "4.0.9" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.9.tgz#44ac4c48121bc90e71cb2947e1ea1a6c222ccd7f" + integrity sha512-2Y3QfiAM37WvDMjkVNaRtZgxVzWKj73HE61YQ/95T53yle+CRwTVSl6Gbv/lWVKXeZcM5af9n9TDVf0k7Xh+cw== dependencies: compare-func "^1.3.1" conventional-commits-filter "^2.0.2" dateformat "^3.0.0" - handlebars "^4.1.2" + handlebars "^4.4.0" json-stringify-safe "^5.0.1" lodash "^4.2.1" meow "^4.0.0" @@ -2219,15 +2209,7 @@ conventional-changelog-writer@^4.0.0: split "^1.0.0" through2 "^3.0.0" -conventional-commits-filter@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz#55a135de1802f6510b6758e0a6aa9e0b28618db3" - integrity sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A== - dependencies: - is-subset "^0.1.1" - modify-values "^1.0.0" - -conventional-commits-filter@^2.0.2: +conventional-commits-filter@^2.0.0, conventional-commits-filter@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.2.tgz#f122f89fbcd5bb81e2af2fcac0254d062d1039c1" integrity sha512-WpGKsMeXfs21m1zIw4s9H5sys2+9JccTzpN6toXtxhpw2VNF2JUXwIakthKBy+LN4DvJm+TzWhxOMWOs1OFCFQ== @@ -2249,16 +2231,16 @@ conventional-commits-parser@^2.1.0: trim-off-newlines "^1.0.0" conventional-commits-parser@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz#fe1c49753df3f98edb2285a5e485e11ffa7f2e4c" - integrity sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg== + version "3.0.5" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.5.tgz#df471d6cb3f6fecfd1356ac72e0b577dbdae0a9c" + integrity sha512-qVz9+5JwdJzsbt7JbJ6P7NOXBGt8CyLFJYSjKAuPSgO+5UGfcsbk9EMR+lI8Unlvx6qwIc2YDJlrGIfay2ehNA== dependencies: JSONStream "^1.0.4" - is-text-path "^1.0.0" + is-text-path "^2.0.0" lodash "^4.2.1" meow "^4.0.0" split2 "^2.0.0" - through2 "^2.0.0" + through2 "^3.0.0" trim-off-newlines "^1.0.0" convert-source-map@^1.1.0, convert-source-map@^1.6.0: @@ -2359,13 +2341,13 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: which "^1.2.9" cross-spawn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.0.tgz#21ef9470443262f33dba80b2705a91db959b2e03" - integrity sha512-6U/8SMK2FBNnB21oQ4+6Nsodxanw1gTkntYA2zBdkFYFu3ZDx65P2ONEXGSvob/QS6REjVHQ9zxzdOafwFdstw== + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== dependencies: path-key "^3.1.0" - shebang-command "^1.2.0" - which "^1.2.9" + shebang-command "^2.0.0" + which "^2.0.1" crypt@~0.0.1: version "0.0.2" @@ -2620,23 +2602,23 @@ dir-glob@^3.0.0, dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -disposable-email-domains@^1.0.48: - version "1.0.48" - resolved "https://registry.yarnpkg.com/disposable-email-domains/-/disposable-email-domains-1.0.48.tgz#2043b8035119f6bdc9eb9057da00cbb856357073" - integrity sha512-o/4hN5KW5RDwf+d8+SZ28sRCJfZauf/zBtCMrCiSjsKdeAGkqw8YtNbWUpRUia8bbTN+HObPrf9mOVruN6yumg== +disposable-email-domains@^1.0.49: + version "1.0.49" + resolved "https://registry.yarnpkg.com/disposable-email-domains/-/disposable-email-domains-1.0.49.tgz#283b5983ecee839d7228b0dda7634e4162865016" + integrity sha512-HbhPQeQ9zsK/vT5YpAW4c0Yt7EspujDcqCeKHNbc5Mkl9O2engXmbAQ1K8906L8RoNVGUuavZ4EEUEVCyCEmdw== -dlock@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dlock/-/dlock-10.0.0.tgz#ac53f1bee430fec9053ae9abfd52474e2ed31356" - integrity sha512-gJeZTKPnteFUDl4YthFAm/+yzVQ+JfY1NiinhVTWozqwAFqknAgYGL6+Gwme+Jig+ZnMxCrqmlgJcfU9ktxIZA== +dlock@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/dlock/-/dlock-11.0.0.tgz#c652d9ebc790d9299890f18444b640b49138426d" + integrity sha512-hbJ1jlVOwVtBZ/OFquAzKYUCcLYU9W3s9T3q4B7fxO9ZtIXQmpGkyMvdcQgIpuFjzDYSGX0vh3t+gQ+E8bU9bw== dependencies: - bluebird "^3.5.5" + bluebird "^3.7.1" bunyan "^1.8.12" callback-queue "^3.0.0" denque "^1.4.1" ioredis-lock "^4.0.0" lodash "^4.17.15" - serialize-error "^4.1.0" + serialize-error "^5.0.0" doctrine@1.5.0: version "1.5.0" @@ -2780,6 +2762,11 @@ emoji-regex@^7.0.1: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + enabled@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" @@ -2795,9 +2782,9 @@ encoding@^0.1.11: iconv-lite "~0.4.13" end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" @@ -2812,9 +2799,9 @@ entities@^2.0.0: integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== env-ci@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-4.1.1.tgz#b8438fc7258a0dc7a4f4c4816de730767946a718" - integrity sha512-eTgpkALDeYRGNhYM2fO9LKsWDifoUgKL7hxpPZqFMP2IU7f+r89DtKqCmk3yQB/jxS8CmZTfKnWO5TiIDFs9Hw== + version "4.5.0" + resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-4.5.0.tgz#96fdb4fde14cfc16b70126da1be7853440e0d6ec" + integrity sha512-0b5ihp/O/tsxWvzEY/Ags+3SL+F9eFci9ZF2Mqx/NHYCaV3cfpoPZW8qx1fcQAHOjWD9wSWsByewzimvGar/4Q== dependencies: execa "^1.0.0" java-properties "^1.0.0" @@ -2849,9 +2836,9 @@ error-ex@^1.2.0, error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" - integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== + version "1.15.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.15.0.tgz#8884928ec7e40a79e3c9bc812d37d10c8b24cc57" + integrity sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ== dependencies: es-to-primitive "^1.2.0" function-bind "^1.1.1" @@ -2861,8 +2848,8 @@ es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: is-regex "^1.0.4" object-inspect "^1.6.0" object-keys "^1.1.1" - string.prototype.trimleft "^2.0.0" - string.prototype.trimright "^2.0.0" + string.prototype.trimleft "^2.1.0" + string.prototype.trimright "^2.1.0" es-to-primitive@^1.2.0: version "1.2.0" @@ -2895,6 +2882,14 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +eslint-ast-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-ast-utils/-/eslint-ast-utils-1.1.0.tgz#3d58ba557801cfb1c941d68131ee9f8c34bd1586" + integrity sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA== + dependencies: + lodash.get "^4.4.2" + lodash.zip "^4.2.0" + eslint-config-airbnb-base@14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz#8a7bcb9643d13c55df4dd7444f138bf4efa61e17" @@ -2904,15 +2899,16 @@ eslint-config-airbnb-base@14.0.0: object.assign "^4.1.0" object.entries "^1.1.0" -eslint-config-makeomatic@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-makeomatic/-/eslint-config-makeomatic-3.1.0.tgz#34bd525f5aa4582a407074aaaa47f8364cf3995c" - integrity sha512-wGhJxuoB3YDWdPhPFsxWOLPFot2SSbztmYNdnLH7om8Cd6JDZAF0vDGQr6TeV+r3iKfgUFTniFL4qO+Ppbx9sg== +eslint-config-makeomatic@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-makeomatic/-/eslint-config-makeomatic-4.0.0.tgz#ff4af7643e405f423c95f4c6216bacc85c3643db" + integrity sha512-ms2tjdJhrAvSU8gSLbcg3l8kRyDPiNenk3ornM+HOtci4I4l8Yv+aBHVkTJFFOfupdD5oZYpcjUwgnNckyP7dQ== dependencies: - eslint "6.2.1" + eslint "6.5.1" eslint-config-airbnb-base "14.0.0" eslint-plugin-import "2.18.2" eslint-plugin-promise "4.2.1" + eslint-plugin-unicorn "^12.1.0" eslint-import-resolver-node@^0.3.2: version "0.3.2" @@ -2947,10 +2943,10 @@ eslint-plugin-import@2.18.2, eslint-plugin-import@^2.18.2: read-pkg-up "^2.0.0" resolve "^1.11.0" -eslint-plugin-mocha@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-6.1.1.tgz#5a036f2f806e1a5fb7d19f7538ebeff3afb15377" - integrity sha512-p/otruG425jRYDa28HjbBYYXoFNzq3Qp++gn5dbE44Kz4NvmIsSUKSV1T+RLYUcZOcdJKKAftXbaqkHFqReKoA== +eslint-plugin-mocha@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.1.tgz#f2ae6d82511291949e440a34b57df773cb4b5ede" + integrity sha512-o3Ibhpczi5MjUVpnlnrpC/+oJYGoHKB5m4bQdRnaAOeFCN3HRkqBisQ2/h0hEuCR4lPxyHP1Qzyjpna8MsOdlA== dependencies: ramda "^0.26.1" @@ -2959,6 +2955,28 @@ eslint-plugin-promise@4.2.1, eslint-plugin-promise@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== +eslint-plugin-unicorn@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-12.1.0.tgz#6ebff6c90ecf4df7ce1615e18928d10bb50c2ff5" + integrity sha512-DkPRrjaZaKa8GDjEyWGms/sqp2DcmVCcbwVi9WQXwN6+Sn0/joTC14SfA+BsCuxTaGPRm/7wa8NC8o5mNDyZpQ== + dependencies: + ci-info "^2.0.0" + clean-regexp "^1.0.0" + eslint-ast-utils "^1.1.0" + eslint-template-visitor "^1.0.0" + import-modules "^2.0.0" + lodash.camelcase "^4.3.0" + lodash.defaultsdeep "^4.6.1" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.topairs "^4.3.0" + lodash.upperfirst "^4.3.1" + read-pkg-up "^7.0.0" + regexpp "^3.0.0" + reserved-words "^0.1.2" + safe-regex "^2.0.2" + semver "^6.3.0" + eslint-scope@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" @@ -2967,6 +2985,15 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-template-visitor@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-template-visitor/-/eslint-template-visitor-1.1.0.tgz#f090d124d1a52e05552149fc50468ed59608b166" + integrity sha512-Lmy6QVlmFiIGl5fPi+8ACnov3sare+0Ouf7deJAGGhmUfeWJ5fVarELUxZRpsZ9sHejiJUq8626d0dn9uvcZTw== + dependencies: + eslint-visitor-keys "^1.1.0" + espree "^6.1.1" + multimap "^1.0.2" + eslint-utils@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" @@ -2974,15 +3001,22 @@ eslint-utils@^1.4.2: dependencies: eslint-visitor-keys "^1.0.0" +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== -eslint@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.2.1.tgz#66c2e4fe8b6356b9f01e828adc3ad04030122df1" - integrity sha512-ES7BzEzr0Q6m5TK9i+/iTpKjclXitOdDK4vT07OqbkBT2/VcN/gO9EL1C4HlK3TAOXYv2ItcmbVR9jO1MR0fJg== +eslint@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" + integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -2993,7 +3027,7 @@ eslint@6.2.1: eslint-scope "^5.0.0" eslint-utils "^1.4.2" eslint-visitor-keys "^1.1.0" - espree "^6.1.0" + espree "^6.1.1" esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^5.0.1" @@ -3022,10 +3056,10 @@ eslint@6.2.1: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" - integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== +eslint@^6.6.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.6.0.tgz#4a01a2fb48d32aacef5530ee9c5a78f11a8afd04" + integrity sha512-PpEBq7b6qY/qrOmpYQ/jTMDYfuQMELR4g4WI1M/NaSDDD/bdcMb+dj4Hgks7p41kW2caXsPsEZAEAyAgjVVC0g== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -3034,9 +3068,9 @@ eslint@^6.5.1: debug "^4.0.1" doctrine "^3.0.0" eslint-scope "^5.0.0" - eslint-utils "^1.4.2" + eslint-utils "^1.4.3" eslint-visitor-keys "^1.1.0" - espree "^6.1.1" + espree "^6.1.2" esquery "^1.0.1" esutils "^2.0.2" file-entry-cache "^5.0.1" @@ -3046,7 +3080,7 @@ eslint@^6.5.1: ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" - inquirer "^6.4.1" + inquirer "^7.0.0" is-glob "^4.0.0" js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" @@ -3065,7 +3099,7 @@ eslint@^6.5.1: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^6.1.0, espree@^6.1.1: +espree@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== @@ -3074,6 +3108,15 @@ espree@^6.1.0, espree@^6.1.1: acorn-jsx "^5.0.2" eslint-visitor-keys "^1.1.0" +espree@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + integrity sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA== + dependencies: + acorn "^7.1.0" + acorn-jsx "^5.1.0" + eslint-visitor-keys "^1.1.0" + esprima@^1.2.0: version "1.2.5" resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.5.tgz#0993502feaf668138325756f30f9a51feeec11e9" @@ -3150,11 +3193,11 @@ execa@^1.0.0: strip-eof "^1.0.0" execa@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/execa/-/execa-2.0.4.tgz#2f5cc589c81db316628627004ea4e37b93391d8e" - integrity sha512-VcQfhuGD51vQUQtKIq2fjGDLDbL6N1DTQVpYzxZ7LPIXw3HqTuIz6uxRmpV1qf8i31LHf2kjiaGI+GdHwRgbnQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-2.1.0.tgz#e5d3ecd837d2a60ec50f3da78fd39767747bbe99" + integrity sha512-Y/URAVapfbYy2Xp/gb6A0E7iR8xeqOCXsuuaoMn7A5PzrXUK84E1gyiEfq0wQd/GHA6GsoHWwhNq8anb0mleIw== dependencies: - cross-spawn "^6.0.5" + cross-spawn "^7.0.0" get-stream "^5.0.0" is-stream "^2.0.0" merge-stream "^2.0.0" @@ -3164,6 +3207,22 @@ execa@^2.0.2: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.2.0.tgz#18326b79c7ab7fbd6610fd900c1b9e95fa48f90a" + integrity sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -3251,15 +3310,14 @@ fast-deep-equal@^2.0.1: integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= fast-glob@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.0.4.tgz#d484a41005cb6faeb399b951fd1bd70ddaebb602" - integrity sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg== + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.1.0.tgz#77375a7e3e6f6fc9b18f061cddd28b8d1eec75ae" + integrity sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw== dependencies: - "@nodelib/fs.stat" "^2.0.1" - "@nodelib/fs.walk" "^1.2.1" - glob-parent "^5.0.0" - is-glob "^4.0.1" - merge2 "^1.2.3" + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" micromatch "^4.0.2" fast-json-stable-stringify@^2.0.0: @@ -3272,17 +3330,12 @@ fast-levenshtein@~2.0.4: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= -fast-redact@^1.4.4: - version "1.5.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-1.5.0.tgz#302892f566750c4f5eec7b830bfc9bc473484034" - integrity sha512-Afo61CgUjkzdvOKDHn08qnZ0kwck38AOGcMlvSGzvJbIab6soAP5rdoQayecGCDsD69AiF9vJBXyq31eoEO2tQ== - -fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2" - integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg== +fast-redact@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-2.0.0.tgz#17bb8f5e1f56ecf4a38c8455985e5eab4c478431" + integrity sha512-zxpkULI9W9MNTK2sJ3BpPQrTEXFNESd2X6O1tXMFpK/XM0G5c5Rll2EVYZH2TqI3xRGK/VaJ+eEOt7pnENJpeA== -fast-safe-stringify@^2.0.7: +fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== @@ -3510,11 +3563,11 @@ fs-extra@^8.0.0: universalify "^0.1.0" fs-minipass@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.6.tgz#2c5cc30ded81282bfe8a0d7c7c1853ddeb102c07" - integrity sha512-crhvyXcMejjv3Z5d2Fa9sf5xLYVCF5O1c71QxbVnbLsmYMBEvDAftewesN/HhY03YRoA7zOMxjNGrF5svGaaeQ== + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== dependencies: - minipass "^2.2.1" + minipass "^2.6.0" fs-readdir-recursive@^1.1.0: version "1.1.0" @@ -3553,7 +3606,7 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" -function-bind@^1.0.2, function-bind@^1.1.1: +function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== @@ -3694,10 +3747,10 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== +glob-parent@^5.0.0, glob-parent@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== dependencies: is-glob "^4.0.1" @@ -3736,6 +3789,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.5.tgz#6714c69bee20f3c3e64c4dd905553e532b40cdc0" + integrity sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^0.1.0, global-dirs@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" @@ -3789,10 +3854,10 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -handlebars@^4.1.2, handlebars@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.2.0.tgz#57ce8d2175b9bbb3d8b3cf3e4217b1aec8ddcb2e" - integrity sha512-Kb4xn5Qh1cxAKvQnzNWZ512DhABzyFNmsaJf3OAkWNa4NkaqWcNI8Tao8Tasi0/F4JD9oyG0YxuFyvyR57d+Gw== +handlebars@^4.1.2, handlebars@^4.2.0, handlebars@^4.4.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.4.3.tgz#180bae52c1d0e9ec0c15d7e82a4362d662762f6e" + integrity sha512-B0W4A2U1ww3q7VVthTKfh+epHx+q4mCt6iK+zEAzbMBpWQAwxCeKxEGpj/1oQTpzPXDNSOG7hmG14TsISH50yw== dependencies: neo-async "^2.6.0" optimist "^0.6.1" @@ -3800,10 +3865,10 @@ handlebars@^4.1.2, handlebars@^4.2.0: optionalDependencies: uglify-js "^3.1.4" -handlebars@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.4.2.tgz#8810a9821a9d6d52cb2f57d326d6ce7c3dfe741d" - integrity sha512-cIv17+GhL8pHHnRJzGu2wwcthL5sb8uDKBHvZ2Dtu5s1YNt0ljbzKbamnc+gr69y7bzwQiBdr5+hOpRd5pnOdg== +handlebars@^4.5.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.1.tgz#8a01c382c180272260d07f2d1aa3ae745715c7ba" + integrity sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA== dependencies: neo-async "^2.6.0" optimist "^0.6.1" @@ -3904,15 +3969,15 @@ hook-std@^2.0.0: resolved "https://registry.yarnpkg.com/hook-std/-/hook-std-2.0.0.tgz#ff9aafdebb6a989a354f729bb6445cf4a3a7077c" integrity sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g== -hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.2: - version "2.8.4" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546" - integrity sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ== +hosted-git-info@^2.1.4, hosted-git-info@^2.7.1, hosted-git-info@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" + integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== hosted-git-info@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.0.tgz#dd8af49cd01e73cc8e61ba13e217a772fd4ecd2d" - integrity sha512-zYSx1cP4MLsvKtTg8DF/PI6e6FHZ3wcawcTGsrLU2TM+UfD4jmSrn2wdQT16TFbH3lO4PIdjLG0E+cuYDgFD9g== + version "3.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.2.tgz#8b7e3bd114b59b51786f8bade0f39ddc80275a97" + integrity sha512-ezZMWtHXm7Eb7Rq4Mwnx2vs79WUx2QmRg3+ZqeGroKzfDO+EprOcgRPYghsOP9JuYBfK18VojmRTGCg8Ma+ktw== dependencies: lru-cache "^5.1.1" @@ -3958,6 +4023,19 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz#0106efa5d63d6d6f3ab87c999fa4877a3fd1ff97" + integrity sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -3965,20 +4043,20 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.5.tgz#d7db27c346645a8dc52df02aa534a377ad7925e0" - integrity sha512-cKd09Jy9cDyNIvAdN2QQAP/oA21sle4FWXjIMDttailpLAYZuBE7WaPmhrkj+afS8Sj9isghAtFvWSQ0JiwOHg== +husky@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/husky/-/husky-3.0.9.tgz#a2c3e9829bfd6b4957509a9500d2eef5dbfc8044" + integrity sha512-Yolhupm7le2/MqC1VYLk/cNmYxsSsqKkTyBhzQHhPK1jFnC89mmmNVuGtLNabjDI6Aj8UNIr0KpRNuBkiC4+sg== dependencies: chalk "^2.4.2" + ci-info "^2.0.0" cosmiconfig "^5.2.1" execa "^1.0.0" get-stdin "^7.0.0" - is-ci "^2.0.0" opencollective-postinstall "^2.0.2" pkg-dir "^4.2.0" please-upgrade-node "^3.2.0" - read-pkg "^5.1.1" + read-pkg "^5.2.0" run-node "^1.0.0" slash "^3.0.0" @@ -4008,9 +4086,9 @@ iferr@^1.0.2: integrity sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg== ignore-walk@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.2.tgz#99d83a246c196ea5c93ef9315ad7b0819c35069b" - integrity sha512-EXyErtpHbn75ZTsOADsfx6J/FPo6/5cjev46PXrcTpd8z3BoRkXgYu9/JVqrI7tusjmwCZutGeRJeU0Wo1e4Cw== + version "3.0.3" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" + integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== dependencies: minimatch "^3.0.4" @@ -4052,16 +4130,26 @@ import-lazy@^2.1.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= +import-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-modules/-/import-modules-2.0.0.tgz#9c1e13b4e7a15682f70a6e3fa29534e4540cfc5d" + integrity sha512-iczM/v9drffdNnABOKwj0f9G3cFDon99VcG1mxeBsdqnbd+vnQ5c2uAiCHNQITqFTOPaEvwg3VjoWCur0uHLEw== + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^3.0.0, indent-string@^3.2.0: +indent-string@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -4118,24 +4206,48 @@ inquirer@^6.4.1: strip-ansi "^5.1.0" through "^2.3.6" +inquirer@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a" + integrity sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + interpret@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== into-stream@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-5.1.0.tgz#b05f37d8fed05c06a0b43b556d74e53e5af23878" - integrity sha512-cbDhb8qlxKMxPBk/QxTtYg1DQ4CwXmadu7quG3B7nrJsgSncEreF2kwWKZFdnjc/lSNNIkFPsjI7SM0Cx/QXPw== + version "5.1.1" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-5.1.1.tgz#f9a20a348a11f3c13face22763f2d02e127f4db8" + integrity sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA== dependencies: from2 "^2.3.0" - p-is-promise "^2.0.0" + p-is-promise "^3.0.0" invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + ioredis-lock@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/ioredis-lock/-/ioredis-lock-4.0.0.tgz#1cea00afbf33729a3d7f525aef4eb2d081d13426" @@ -4207,9 +4319,9 @@ is-buffer@^1.1.5, is-buffer@~1.1.1: integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-buffer@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" - integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" + integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== is-callable@^1.1.4: version "1.1.4" @@ -4223,13 +4335,6 @@ is-ci@^1.0.10: dependencies: ci-info "^1.5.0" -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - is-cidr@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-cidr/-/is-cidr-3.1.0.tgz#72e233d8e1c4cd1d3f11713fcce3eba7b0e3476f" @@ -4308,6 +4413,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -4415,11 +4525,6 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= - is-symbol@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" @@ -4434,6 +4539,13 @@ is-text-path@^1.0.0: dependencies: text-extensions "^1.0.0" +is-text-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-2.0.0.tgz#b2484e2b720a633feb2e85b67dc193ff72c75636" + integrity sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw== + dependencies: + text-extensions "^2.0.0" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -4445,9 +4557,9 @@ is-windows@^1.0.2: integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== is-wsl@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.0.tgz#94369bbeb2249ef07b831b1b08590e686330ccbb" - integrity sha512-pFTjpv/x5HRj8kbZ/Msxi9VrvtOMRBqaDi3OIcbwPI3OuH+r3lLxVWukLITBaOGJIbA/w2+M1eVmVa4XNQlAmQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" + integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== is@^3.2.1, is@^3.3.0: version "3.3.0" @@ -4491,10 +4603,10 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -issue-parser@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/issue-parser/-/issue-parser-4.0.0.tgz#397817323abbb70c7c29cea2ff62448cf83b686c" - integrity sha512-1RmmAXHl5+cqTZ9dRr861xWy0Gkc9TWTEklgjKv+nhlB1dY1NmGBV8b20jTWRL5cPGpOIXkz84kEcDBM8Nc0cw== +issue-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/issue-parser/-/issue-parser-5.0.0.tgz#0e22a40bc275b6c7da6ddf4a9b979e8ca9faf0d4" + integrity sha512-q/16W7EPHRL0FKVz9NU++TUsoygXGj6JOi88oulyAcQG+IEZ0T6teVdE+VLbe19OfL/tbV8Wi3Dfo0HedeHW0Q== dependencies: lodash.capitalize "^4.2.1" lodash.escaperegexp "^4.1.2" @@ -4622,9 +4734,9 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== dependencies: minimist "^1.2.0" @@ -4757,6 +4869,13 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + leven@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" @@ -4770,10 +4889,10 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -libcipm@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/libcipm/-/libcipm-4.0.3.tgz#6a6db4a6e040e56f4af18bb1d664e05e8eb23a39" - integrity sha512-nuIxNtqA+kIkwUiNM/nZ0yPyR7NkSUov6g6mCfFPkYylO1dEovZBL+NZ3axdouS2UOTa8GdnJ7/meSc1/0AIGw== +libcipm@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/libcipm/-/libcipm-4.0.7.tgz#76cd675c98bdaae64db88b782b01b804b6d02c8a" + integrity sha512-fTq33otU3PNXxxCTCYCYe7V96o59v/o7bvtspmbORXpgFk+wcWrGf5x6tBgui5gCed/45/wtPomBsZBYm5KbIw== dependencies: bin-links "^1.1.2" bluebird "^3.5.1" @@ -4857,9 +4976,9 @@ libnpmorg@^1.0.1: npm-registry-fetch "^4.0.0" libnpmpublish@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-1.1.2.tgz#4201cfc4a69c44e6f454ec548fa1cd90f10df0a0" - integrity sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g== + version "1.1.3" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-1.1.3.tgz#e3782796722d79eef1a0a22944c117e0c4ca4280" + integrity sha512-/3LsYqVc52cHXBmu26+J8Ed7sLs/hgGVFMH1mwYpL7Qaynb9RenpKqIKu0sJ130FB9PMkpMlWjlbtU8A4m7CQw== dependencies: aproba "^2.0.0" figgy-pudding "^3.5.1" @@ -5005,6 +5124,11 @@ lodash._root@~3.0.0: resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + lodash.capitalize@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" @@ -5025,6 +5149,11 @@ lodash.defaults@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= +lodash.defaultsdeep@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6" + integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA== + lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" @@ -5085,6 +5214,11 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" + integrity sha1-hImxyw0p/4gZXM7KRI/21swpXDY= + lodash.merge@^4.6.1, lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -5120,6 +5254,11 @@ lodash.set@^4.3.2: resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= + lodash.template@^4.0.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -5140,6 +5279,11 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.topairs@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.topairs/-/lodash.topairs-4.3.0.tgz#3b6deaa37d60fb116713c46c5f17ea190ec48d64" + integrity sha1-O23qo31g+xFnE8RsXxfqGQ7EjWQ= + lodash.union@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -5155,11 +5299,21 @@ lodash.uniqby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" + integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984= + lodash.without@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= +lodash.zip@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" + integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= + lodash@4.17.14: version "4.17.14" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" @@ -5263,6 +5417,13 @@ make-fetch-happen@^5.0.0: socks-proxy-agent "^4.0.0" ssri "^6.0.0" +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5337,12 +5498,14 @@ meant@~1.0.1: resolved "https://registry.yarnpkg.com/meant/-/meant-1.0.1.tgz#66044fea2f23230ec806fb515efea29c44d2115d" integrity sha512-UakVLFjKkbbUwNWJ2frVLnnAtbb7D7DsloxRd3s/gDpI8rdv8W5Hp3NaDb+POBI1fQdeussER6NB8vpcRURvlg== -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== dependencies: - mimic-fn "^1.0.0" + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" meow@5.0.0: version "5.0.0" @@ -5386,10 +5549,10 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.4.tgz#c9269589e6885a60cf80605d9522d4b67ca646e3" - integrity sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A== +merge2@^1.2.3, merge2@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" @@ -5424,9 +5587,9 @@ mime-db@1.40.0: integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== mime-db@1.x.x: - version "1.41.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.41.0.tgz#9110408e1f6aa1b34aef51f2c9df3caddf46b6a0" - integrity sha512-B5gxBI+2K431XW8C2rcc/lhppbuji67nf9v39eH8pkWoZDxnAL0PxdpH32KYRScniF8qDHBDlI+ipgg5WrCUYw== + version "1.42.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" + integrity sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ== mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.24" @@ -5445,7 +5608,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.1.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -5480,20 +5643,20 @@ minimist@~0.0.1: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -minipass@^2.2.1, minipass@^2.3.5: - version "2.5.1" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.5.1.tgz#cf435a9bf9408796ca3a3525a8b851464279c9b8" - integrity sha512-dmpSnLJtNQioZFI5HfQ55Ad0DzzsMAb+HfokwRTNXwEQjepbTkl5mtIlSVxGIkOkxlpX7wIn5ET/oAd9fZ/Y/Q== +minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== dependencies: safe-buffer "^5.1.2" yallist "^3.0.0" minizlib@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" - integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== dependencies: - minipass "^2.2.1" + minipass "^2.9.0" mississippi@^3.0.0: version "3.0.0" @@ -5526,10 +5689,10 @@ mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -mocha@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.1.tgz#da941c99437da9bac412097859ff99543969f94c" - integrity sha512-VCcWkLHwk79NYQc8cxhkmI8IigTIhsCwZ6RTxQsqK6go4UvEhzJkYuHm8B2YtlSxcYq2fY+ucr4JBwoD6ci80A== +mocha@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" + integrity sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A== dependencies: ansi-colors "3.2.3" browser-stdout "1.3.1" @@ -5598,10 +5761,10 @@ ms-conf@^5.0.2: lodash.uniq "^4.5.0" nconf "^0.10.0" -ms-flakeless@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/ms-flakeless/-/ms-flakeless-4.2.0.tgz#3182dc0fb9bf871e170a56c392f0bd92280424b3" - integrity sha512-Py7Ga/1YRb5DVuWLKrlFT7OWNnLwEKb52qR1tyuqSVPuMGdXlfRlqxcBaeG82PfDh2vC/WhcMZqA2UkaRJ6CZg== +ms-flakeless@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ms-flakeless/-/ms-flakeless-4.3.0.tgz#01e33c8b6597894dfdb53dfc2673b49ea8489797" + integrity sha512-nCjuusuttC8q3O3iYPOU8GadRxlQ4JjD1Ot6G/FjnnodGtI0mNJXrH2XP54t5bACb5XOVgCjnyrlWtuH2bXHWA== dependencies: nan "^2.14.0" @@ -5651,12 +5814,17 @@ ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multimap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/multimap/-/multimap-1.0.2.tgz#6aa76fc3233905ba948bbe4c74dc2c3a0356eb36" + integrity sha1-aqdvwyM5BbqUi75MdNwsOgNW6zY= + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -mute-stream@~0.0.4: +mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== @@ -5670,7 +5838,7 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -nan@^2.0.8, nan@^2.12.1, nan@^2.14.0: +nan@^2.12.1, nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -5796,10 +5964,10 @@ node-fetch@^2.2.0, node-fetch@^2.3.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -node-gyp@^5.0.2, node-gyp@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.0.3.tgz#80d64c23790244991b6d44532f0a351bedd3dd45" - integrity sha512-z/JdtkFGUm0QaQUusvloyYuGDub3nUbOo5de1Fz57cM++osBTvQatBUSTlF1k/w8vFHPxxXW6zxGvkxXSpaBkQ== +node-gyp@^5.0.2, node-gyp@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-5.0.5.tgz#f6cf1da246eb8c42b097d7cd4d6c3ce23a4163af" + integrity sha512-WABl9s4/mqQdZneZHVWVG4TVr6QQJZUC6PAx47ITSk9lreZ1n+7Z9mMAIbA3vnO4J9W20P7LhCxtzfWsAD/KDw== dependencies: env-paths "^1.0.0" glob "^7.0.3" @@ -5810,7 +5978,7 @@ node-gyp@^5.0.2, node-gyp@^5.0.3: request "^2.87.0" rimraf "2" semver "~5.3.0" - tar "^4.4.8" + tar "^4.4.12" which "1" node-modules-regexp@^1.0.0: @@ -5872,9 +6040,9 @@ normalize-path@^3.0.0: integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.3.0.tgz#9c49e10fc1876aeb76dba88bf1b2b5d9fa57b2ee" - integrity sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== npm-audit-report@^1.3.2: version "1.3.2" @@ -5894,17 +6062,17 @@ npm-cache-filename@~1.0.2: resolved "https://registry.yarnpkg.com/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz#ded306c5b0bfc870a9e9faf823bc5f283e05ae11" integrity sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE= -npm-install-checks@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-3.0.0.tgz#d4aecdfd51a53e3723b7b2f93b2ee28e307bc0d7" - integrity sha1-1K7N/VGlPjcjt7L5Oy7ijjB7wNc= +npm-install-checks@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-3.0.2.tgz#ab2e32ad27baa46720706908e5b14c1852de44d9" + integrity sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg== dependencies: semver "^2.3.0 || 3.x || 4 || 5" -npm-lifecycle@^3.0.0, npm-lifecycle@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.3.tgz#09e9b0b6686e85fd53bab82364386222d97a3730" - integrity sha512-M0QmmqbEHBXxDrmc6X3+eKjW9+F7Edg1ENau92WkYw1sox6wojHzEZJIRm1ItljEiaigZlKL8mXni/4ylAy1Dg== +npm-lifecycle@^3.0.0, npm-lifecycle@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-3.1.4.tgz#de6975c7d8df65f5150db110b57cce498b0b604c" + integrity sha512-tgs1PaucZwkxECGKhC/stbEgFyc3TGh2TJcg2CDr6jbvQRdteHNhmMeljRzpe4wgFAXQADoy1cSqqi7mtiAa5A== dependencies: byline "^5.0.0" graceful-fs "^4.1.15" @@ -5931,9 +6099,9 @@ npm-logical-tree@^1.2.1: validate-npm-package-name "^3.0.0" npm-packlist@^1.1.12, npm-packlist@^1.1.6, npm-packlist@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.4.tgz#866224233850ac534b63d1a6e76050092b5d2f44" - integrity sha512-zTLo8UcVYtDU3gdeaFu2Xu0n0EvelfHDGuqtNIn5RO7yQj4H1TqNdBc/yZjxnWA0PVB8D3Woyp0i5B43JwQ6Vw== + version "1.4.6" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.6.tgz#53ba3ed11f8523079f1457376dd379ee4ea42ff4" + integrity sha512-u65uQdb+qwtGvEJh/DgQgW1Xg7sqeNbmxYyrvlNznaVTjV3E5P6F/EFjM+BVHXl7JJlsdG8A64M0XI8FI/IOlg== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -5964,9 +6132,9 @@ npm-profile@^4.0.2: npm-registry-fetch "^4.0.0" npm-registry-fetch@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.0.tgz#5ef75845b605855c7964472542c25da172af8677" - integrity sha512-Jllq35Jag8dtv0M17ue74XtdQTyqKzuAYGiX9mAjOhkmNjib3bBUgK6mUY61+AHnXeSRobQkpY3/xIOS/omptw== + version "4.0.2" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.2.tgz#2b1434f93ccbe6b6385f8e45f45db93e16921d7a" + integrity sha512-Z0IFtPEozNdeZRPh3aHHxdG+ZRpzcbQaJLthsm3VhNf6DScicTFRHZzK82u8RsJUsUHkX+QH/zcB/5pmd20H4A== dependencies: JSONStream "^1.3.4" bluebird "^3.5.1" @@ -5974,6 +6142,7 @@ npm-registry-fetch@^4.0.0: lru-cache "^5.1.1" make-fetch-happen "^5.0.0" npm-package-arg "^6.1.0" + safe-buffer "^5.2.0" npm-run-path@^2.0.0: version "2.0.2" @@ -5989,15 +6158,22 @@ npm-run-path@^3.0.0: dependencies: path-key "^3.0.0" +npm-run-path@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.0.tgz#d644ec1bd0569187d2a52909971023a0a58e8438" + integrity sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ== + dependencies: + path-key "^3.0.0" + npm-user-validate@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.0.tgz#8ceca0f5cea04d4e93519ef72d0557a75122e951" integrity sha1-jOyg9c6gTU6TUZ73LQVXp1Ei6VE= npm@^6.10.3: - version "6.11.3" - resolved "https://registry.yarnpkg.com/npm/-/npm-6.11.3.tgz#730f46b7cc5bbc6f04dd57b5699be0c9f2359dda" - integrity sha512-K2h+MPzZiY39Xf6eHEdECe/LKoJXam4UCflz5kIxoskN3LQFeYs5fqBGT5i4TtM/aBk+86Mcf+jgXs/WuWAutQ== + version "6.12.0" + resolved "https://registry.yarnpkg.com/npm/-/npm-6.12.0.tgz#255e6fbb514be15c6595f9cbc5bc248e251e1476" + integrity sha512-juj5VkB3/k+PWbJUnXD7A/8oc8zLusDnK/sV9PybSalsbOVOTIp5vSE0rz5rQ7BsmUgQS47f/L2GYQnWXaKgnQ== dependencies: JSONStream "^1.3.5" abbrev "~1.1.1" @@ -6029,7 +6205,7 @@ npm@^6.10.3: glob "^7.1.4" graceful-fs "^4.2.2" has-unicode "~2.0.1" - hosted-git-info "^2.8.2" + hosted-git-info "^2.8.5" iferr "^1.0.2" infer-owner "^1.0.4" inflight "~1.0.6" @@ -6039,7 +6215,7 @@ npm@^6.10.3: is-cidr "^3.0.0" json-parse-better-errors "^1.0.2" lazy-property "~1.0.0" - libcipm "^4.0.3" + libcipm "^4.0.4" libnpm "^3.0.1" libnpmaccess "^3.0.2" libnpmhook "^5.0.3" @@ -6059,13 +6235,13 @@ npm@^6.10.3: mississippi "^3.0.0" mkdirp "~0.5.1" move-concurrently "^1.0.1" - node-gyp "^5.0.3" + node-gyp "^5.0.5" nopt "~4.0.1" normalize-package-data "^2.5.0" npm-audit-report "^1.3.2" npm-cache-filename "~1.0.2" - npm-install-checks "~3.0.0" - npm-lifecycle "^3.1.3" + npm-install-checks "^3.0.2" + npm-lifecycle "^3.1.4" npm-package-arg "^6.1.1" npm-packlist "^1.4.4" npm-pick-manifest "^3.0.2" @@ -6099,8 +6275,8 @@ npm@^6.10.3: sorted-object "~2.0.1" sorted-union-stream "~2.1.3" ssri "^6.0.1" - stringify-package "^1.0.0" - tar "^4.4.10" + stringify-package "^1.0.1" + tar "^4.4.12" text-table "~0.2.0" tiny-relative-date "^1.3.0" uid-number "0.0.6" @@ -6291,9 +6467,9 @@ onetime@^5.1.0: mimic-fn "^2.1.0" ono@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/ono/-/ono-5.0.2.tgz#672e25b2d96b60530c62cf18236cbd82e3249eae" - integrity sha512-6XDlkEDQCgHNOdWS5SQlCSEJrJ9Ifh6lGrI8llR/UYOOA83EUVCt+4eWo8LT0rvEfyYSYJG90ExJHnTJqsoPBA== + version "5.1.0" + resolved "https://registry.yarnpkg.com/ono/-/ono-5.1.0.tgz#8cafa7e56afa2211ad63dd2eb798427e64f1a070" + integrity sha512-GgqRIUWErLX4l9Up0khRtbrlH8Fyj59A0nKv8V6pWEto38aUgnOGOOF7UmgFFLzFnDSc8REzaTXOc0hqEe7yIw== opencollective-postinstall@^2.0.2: version "2.0.2" @@ -6342,14 +6518,14 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" os-name@^3.1.0: version "3.1.0" @@ -6388,6 +6564,11 @@ output-file-sync@^2.0.0: is-plain-obj "^1.1.0" mkdirp "^0.5.1" +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + p-filter@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" @@ -6410,6 +6591,11 @@ p-is-promise@^2.0.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== +p-is-promise@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-3.0.0.tgz#58e78c7dfe2e163cf2a04ff869e7c1dba64a5971" + integrity sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ== + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" @@ -6701,19 +6887,19 @@ pino-multi-stream@^4.2.0: dependencies: pino "^5.13.2" -pino-pretty@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-3.2.1.tgz#db13b793f7074f25051306ee625b6feabae056d9" - integrity sha512-PGdcRYw7HCF7ovMhrnepOUmEVh5+tATydRrBICEbP37oRasXV+lo2HA9gg8b7cE7LG6G1OZGVXTZ7MLd946k1Q== +pino-pretty@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-3.2.2.tgz#e252f964f99a0a3bd34f4a484bb4e202632eaaad" + integrity sha512-2SP++Wbo4EsH5RpLhcsYHOR/8CMTIJWZpU0aPiVK0Wq8SrRMQ9oHQNffVK5ZyDFZMtSuTdFCVfLVNFmtFMDEeA== dependencies: "@hapi/bourne" "^1.3.2" args "^5.0.1" chalk "^2.4.2" dateformat "^3.0.3" - fast-safe-stringify "^2.0.6" + fast-safe-stringify "^2.0.7" jmespath "^0.15.0" pump "^3.0.0" - readable-stream "^3.3.0" + readable-stream "^3.4.0" split2 "^3.1.1" pino-std-serializers@^2.3.0: @@ -6722,27 +6908,27 @@ pino-std-serializers@^2.3.0: integrity sha512-WaL504dO8eGs+vrK+j4BuQQq6GLKeCCcHaMB2ItygzVURcL1CycwNEUHTD/lHFHs/NL5qAz2UKrjYWXKSf4aMQ== pino@^5.13.2: - version "5.13.2" - resolved "https://registry.yarnpkg.com/pino/-/pino-5.13.2.tgz#773416c9764634276e7b2ae021357679ff7b5634" - integrity sha512-WwOSCy36/gWhinsqWqAnuwIi2WtcH+jvoyeLm3bjUALIrzWIst0AovQjK4jVvSN2l64KFPfi3gd2fjsTovjdLQ== + version "5.13.4" + resolved "https://registry.yarnpkg.com/pino/-/pino-5.13.4.tgz#52935caaab8d47048deffa315336e8da30d8b96d" + integrity sha512-heeg8m8FZY8Nl3nuuD+msJUmhamqoGl7JXoTExh9YpGajzz6LYbVByUqrjbf4sCEMYFsqdcqnTJWiSY660DraQ== dependencies: - fast-redact "^1.4.4" - fast-safe-stringify "^2.0.6" + fast-redact "^2.0.0" + fast-safe-stringify "^2.0.7" flatstr "^1.0.9" pino-std-serializers "^2.3.0" quick-format-unescaped "^3.0.2" sonic-boom "^0.7.5" -pino@^5.13.3: - version "5.13.3" - resolved "https://registry.yarnpkg.com/pino/-/pino-5.13.3.tgz#26cd6f69b4bd03d6408af28eddcd9313687f143d" - integrity sha512-FL12DKlPwBlbhztlUz6kseR03PRR8nD+wvLdN/Sji9UiBYYfSjX+k8ocU7/NwW55JdFRONTn3iACoelXnMFVVQ== +pino@^5.13.5: + version "5.13.5" + resolved "https://registry.yarnpkg.com/pino/-/pino-5.13.5.tgz#3bfd03c9e7d247adf806960a25c139572ce3cd4f" + integrity sha512-NSArDZnjIXgzTLsYA5EhYwLiMe2OmGJ73760Wt5Vj44kUcuPJk4ub29BKtWXGAMwVmW1cQ7Q8jQaLjY/5Gxqcw== dependencies: - fast-redact "^1.4.4" + fast-redact "^2.0.0" fast-safe-stringify "^2.0.7" flatstr "^1.0.9" pino-std-serializers "^2.3.0" - quick-format-unescaped "^3.0.2" + quick-format-unescaped "^3.0.3" sonic-boom "^0.7.5" pirates@^4.0.0: @@ -6907,14 +7093,14 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-1.20.0.tgz#e3d267786f74e1d87cf2d15acc59177f471bbe38" - integrity sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ== +puppeteer@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.0.0.tgz#0612992e29ec418e0a62c8bebe61af1a64d7ec01" + integrity sha512-t3MmTWzQxPRP71teU6l0jX47PHXlc4Z52sQv4LJQSZLq1ttkKS2yGM3gaI57uQwZkNaoGd0+HPPMELZkcyhlqA== dependencies: debug "^4.1.0" extract-zip "^1.6.6" - https-proxy-agent "^2.2.1" + https-proxy-agent "^3.0.0" mime "^2.0.3" progress "^2.0.1" proxy-from-env "^1.0.0" @@ -6950,10 +7136,10 @@ query-string@^6.8.2: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -quick-format-unescaped@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-3.0.2.tgz#0137e94d8fb37ffeb70040535111c378e75396fb" - integrity sha512-FXTaCkwvpIlkdKeGDNgcq07SXWS383noQUuZjvdE1QcTt+eLuqof6/BDiEPqB59FWLie/l91+HtlJSw7iCViSA== +quick-format-unescaped@^3.0.2, quick-format-unescaped@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-3.0.3.tgz#fb3e468ac64c01d22305806c39f121ddac0d1fb9" + integrity sha512-dy1yjycmn9blucmJLXOfZDx1ikZJUi6E8bBZLnhPG5gBrVhHXx2xVyqqgKBubVNEXmx51dBACMHpoMQK/N/AXQ== quick-lru@^1.0.0: version "1.1.0" @@ -7055,6 +7241,15 @@ read-pkg-up@^6.0.0: read-pkg "^5.1.1" type-fest "^0.5.0" +read-pkg-up@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.0.tgz#3f3e53858ec5ae5e6fe14bc479da0a7c98f85ff3" + integrity sha512-t2ODkS/vTTcRlKwZiZsaLGb5iwfx9Urp924aGzVyboU6+7Z2i6eGr/G1Z4mjvwLLQV3uFOBKobNRGM3ux2PD/w== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + read-pkg@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" @@ -7103,7 +7298,7 @@ read@1, read@~1.0.1, read@~1.0.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.3.0, readable-stream@^3.4.0: +"readable-stream@2 || 3", readable-stream@^3.0.0, readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== @@ -7205,11 +7400,21 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp-tree@~0.1.1: + version "0.1.14" + resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.14.tgz#1abca3675f6cc4b0dee5c959c6c4554ed172dfae" + integrity sha512-59v5A90TAh4cAMyDQEOzcnsu4q7Wb10RsyTjngEnJIZsWYM4siVGu+JmLT1WsxHvOWhiu4YS20XiTuxWMeVoHQ== + regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpp@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e" + integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g== + registry-auth-token@^3.0.1: version "3.4.0" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" @@ -7313,6 +7518,11 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== +reserved-words@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" + integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE= + resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -7355,6 +7565,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -7439,7 +7657,7 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== @@ -7461,6 +7679,13 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-regex@^2.0.2: + version "2.1.1" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" + integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== + dependencies: + regexp-tree "~0.1.1" + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -7471,12 +7696,10 @@ sax@^1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -scrypt@^6.0.1: - version "6.0.3" - resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d" - integrity sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0= - dependencies: - nan "^2.0.8" +scrypt-kdf@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/scrypt-kdf/-/scrypt-kdf-2.0.1.tgz#3355224c52d398331b2cbf2b70a7be26b52c53e6" + integrity sha512-dMhpgBVJPDWZP5erOCwTjI6oAO9hKhFAjZsdSQ0spaWJYHuA/wFNF2weQQfsyCIk8eNKoLfEDxr3zAtM+gZo0Q== secure-keys@^1.0.0: version "1.0.0" @@ -7537,10 +7760,10 @@ semver-regex@^2.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" - integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== +semver@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" + integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== semver@^6.0.0, semver@^6.1.2, semver@^6.3.0: version "6.3.0" @@ -7552,13 +7775,6 @@ semver@~5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= -serialize-error@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-4.1.0.tgz#63e1e33ede20bcd89d9f0528ea4c15fbf0f2b78a" - integrity sha512-5j9GgyGsP9vV9Uj1S0lDCvlsd+gc2LEPVK7HHHte7IyPwOD4lVQFeaX143gx3U5AnoCi+wbcb3mvaxVysjpxEw== - dependencies: - type-fest "^0.3.0" - serialize-error@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac" @@ -7600,11 +7816,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shelljs@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" @@ -7728,10 +7956,10 @@ socks@~2.3.2: ip "^1.1.5" smart-buffer "4.0.2" -sonic-boom@^0.7.5: - version "0.7.5" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-0.7.5.tgz#b383d92cdaaa8e66d1f77bdec71b49806d01b5f1" - integrity sha512-1pKrnAV6RfvntPnarY71tpthFTM3pWZWWQdghZY8ARjtDPGzG/inxqSuRwQY/7V1woUjfyxPb437zn4p5phgnQ== +sonic-boom@^0.7.5, sonic-boom@^0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-0.7.6.tgz#c42df6df884a6a3d54fa7a45b11e4e2196818d45" + integrity sha512-k9E2QQ4zxuVRLDW+ZW6ISzJs3wlEorVdmM7ApDgor7wsGKSDG5YGHsGmgLY4XYh4DMlr/2ap2BWAE7yTFJtWnQ== dependencies: flatstr "^1.0.12" @@ -7989,21 +8217,30 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.trimleft@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.0.0.tgz#68b6aa8e162c6a80e76e3a8a0c2e747186e271ff" - integrity sha1-aLaqjhYsaoDnbjqKDC50cYbicf8= +string-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff" + integrity sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ== dependencies: - define-properties "^1.1.2" - function-bind "^1.0.2" + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^5.2.0" -string.prototype.trimright@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.0.0.tgz#ab4a56d802a01fbe7293e11e84f24dc8164661dd" - integrity sha1-q0pW2AKgH75yk+EehPJNyBZGYd0= +string.prototype.trimleft@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== dependencies: - define-properties "^1.1.2" - function-bind "^1.0.2" + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" string_decoder@^1.1.1: version "1.3.0" @@ -8024,10 +8261,10 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-package@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.0.tgz#e02828089333d7d45cd8c287c30aa9a13375081b" - integrity sha512-JIQqiWmLiEozOC0b0BtxZ/AOUtdUZHCBPgqIZ2kSJJqGwgb9neo44XdTHUC4HZSGqi03hOeB7W/E8rAlKnGe9g== +stringify-package@^1.0.0, stringify-package@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" + integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" @@ -8087,7 +8324,7 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" -supports-color@^5.0.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-color@^5.5.0: +supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -8119,14 +8356,14 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tar@^4, tar@^4.4.10, tar@^4.4.8: - version "4.4.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.10.tgz#946b2810b9a5e0b26140cf78bea6b0b0d689eba1" - integrity sha512-g2SVs5QIxvo6OLp0GudTqEf05maawKUxXru104iaayWA09551tFCTI8f1Asb4lPfkBr91k07iL4c11XO3/b0tA== +tar@^4, tar@^4.4.10, tar@^4.4.12: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== dependencies: chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.5" + minipass "^2.8.6" minizlib "^1.2.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" @@ -8170,6 +8407,11 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== +text-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-2.0.0.tgz#43eabd1b495482fae4a2bf65e5f56c29f69220f6" + integrity sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ== + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -8299,11 +8541,6 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - trim@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" @@ -8343,12 +8580,7 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" - integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ== - -type-fest@^0.5.0: +type-fest@^0.5.0, type-fest@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2" integrity sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw== @@ -8358,7 +8590,7 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.0: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -8374,11 +8606,11 @@ uc.micro@^1.0.1, uc.micro@^1.0.5: integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== uglify-js@^3.1.4: - version "3.6.0" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5" - integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg== + version "3.6.1" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.1.tgz#ae7688c50e1bdcf2f70a0e162410003cf9798311" + integrity sha512-+dSJLJpXBb6oMHP+Yvw8hUgElz4gLTh82XuX68QiJVTXaE5ibl6buzhNkQdYhBlIhozWOC9ge16wyRmjG4TwVQ== dependencies: - commander "~2.20.0" + commander "2.20.0" source-map "~0.6.1" uid-number@0.0.6: @@ -8582,6 +8814,13 @@ which@1, which@1.3.1, which@^1.2.10, which@^1.2.9, which@^1.3.0, which@^1.3.1: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.1.tgz#f1cf94d07a8e571b6ff006aeb91d0300c47ef0a4" + integrity sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w== + dependencies: + isexe "^2.0.0" + wide-align@1.1.3, wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -8719,9 +8958,9 @@ yallist@^2.1.2: integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yargs-parser@13.1.1, yargs-parser@^13.0.0, yargs-parser@^13.1.1: version "13.1.1" @@ -8738,10 +8977,10 @@ yargs-parser@^10.0.0: dependencies: camelcase "^4.1.0" -yargs-parser@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-14.0.0.tgz#42e25777b06718ec99eac2c3a98ad3de73b6818f" - integrity sha512-zn/Mnx+tbFjkCFUodEpjXckNS65NfpB5oyqOkDDEG/8uxlfLZJu2IoBLQFjukUkn9rBbGkVYNzrDh6qy4NUd3g== +yargs-parser@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.0.tgz#cdd7a97490ec836195f59f3f4dbe5ea9e8f75f08" + integrity sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ== dependencies: camelcase "^5.0.0" decamelize "^1.2.0" @@ -8785,7 +9024,7 @@ yargs@13.3.0, yargs@^13.2.2, yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.1" -yargs@14.0.0, yargs@^14.0.0: +yargs@14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.0.0.tgz#ba4cacc802b3c0b3e36a9e791723763d57a85066" integrity sha512-ssa5JuRjMeZEUjg7bEL99AwpitxU/zWGAGpdj0di41pOEmJti8NR6kyUIJBkR78DTYNPZOU08luUo0GTHuB+ow== @@ -8803,15 +9042,15 @@ yargs@14.0.0, yargs@^14.0.0: yargs-parser "^13.1.1" yargs@^11.0.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77" - integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A== + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.1.tgz#5052efe3446a4df5ed669c995886cc0f13702766" + integrity sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw== dependencies: cliui "^4.0.0" decamelize "^1.1.1" find-up "^2.1.0" get-caller-file "^1.0.1" - os-locale "^2.0.0" + os-locale "^3.1.0" require-directory "^2.1.1" require-main-filename "^1.0.1" set-blocking "^2.0.0" @@ -8820,6 +9059,23 @@ yargs@^11.0.0: y18n "^3.2.1" yargs-parser "^9.0.2" +yargs@^14.0.0, yargs@^14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.0.tgz#f116a9242c4ed8668790b40759b4906c276e76c3" + integrity sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg== + dependencies: + cliui "^5.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^15.0.0" + yargs@^3.19.0: version "3.32.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" From a46f9fae309de3b2723b156bd8d47bba8da599a9 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 31 Oct 2019 05:24:32 +0000 Subject: [PATCH 02/13] chore(release): 12.0.0 [skip ci] # [12.0.0](https://github.com/makeomatic/ms-users/compare/v11.4.0...v12.0.0) (2019-10-31) ### Features * upgrade deps for node 12.13.0 ([#432](https://github.com/makeomatic/ms-users/issues/432)) ([d092478](https://github.com/makeomatic/ms-users/commit/d0924786a9274d655f19393033af26c369bf4115)) ### BREAKING CHANGES * removes scrypt for built-in crypto implementation, while API remains the same the underlaying library is slightly different. Node has changed a major version, too --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe3f8ed3f..39ab7e363 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# [12.0.0](https://github.com/makeomatic/ms-users/compare/v11.4.0...v12.0.0) (2019-10-31) + + +### Features + +* upgrade deps for node 12.13.0 ([#432](https://github.com/makeomatic/ms-users/issues/432)) ([d092478](https://github.com/makeomatic/ms-users/commit/d0924786a9274d655f19393033af26c369bf4115)) + + +### BREAKING CHANGES + +* removes scrypt for built-in crypto implementation, while API remains the same the underlaying library is slightly different. Node has changed a major version, too + # [11.4.0](https://github.com/makeomatic/ms-users/compare/v11.3.1...v11.4.0) (2019-10-03) diff --git a/package.json b/package.json index f7694d524..743332a8a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "ms-users", "description": "Core of the microservice for handling users", "main": "./lib/index.js", - "version": "11.4.0", + "version": "12.0.0", "scripts": { "compile": "rimraf ./lib; babel -d ./lib --copy-files ./src", "pretest": "yarn compile", From 0b092f605fab1d407d4571cba15ac62fb8091a34 Mon Sep 17 00:00:00 2001 From: pajgo Date: Fri, 20 Sep 2019 16:41:05 +0600 Subject: [PATCH 03/13] feat: update user and organization metadata * REWORK --- .../user_and_organization_meta_update.md | 30 +++ src/actions/alias.js | 12 +- src/actions/ban.js | 35 ++-- .../organization/members/permission.js | 5 +- src/actions/organization/members/remove.js | 4 +- src/actions/register.js | 4 +- src/actions/updateMetadata.js | 15 +- src/auth/oauth/utils/attach.js | 30 +-- src/auth/oauth/utils/detach.js | 30 +-- src/constants.js | 2 + src/custom/cappasity-users-activate.js | 7 +- src/custom/rfx-create-room-on-activate.js | 8 +- src/utils/metadata/organization.js | 27 +++ src/utils/metadata/redis/audience.js | 30 +++ src/utils/metadata/redis/update-metadata.js | 173 ++++++++++++++++++ src/utils/metadata/user.js | 58 ++++++ .../organization/add-organization-members.js | 7 +- .../register-organization-members.js | 5 +- src/utils/set-organization-metadata.js | 32 +--- test/suites/ban.js | 3 +- test/suites/update-metadata.js | 78 ++++++-- 21 files changed, 481 insertions(+), 114 deletions(-) create mode 100644 rfcs/inactive_users/user_and_organization_meta_update.md create mode 100644 src/utils/metadata/organization.js create mode 100644 src/utils/metadata/redis/audience.js create mode 100644 src/utils/metadata/redis/update-metadata.js create mode 100644 src/utils/metadata/user.js diff --git a/rfcs/inactive_users/user_and_organization_meta_update.md b/rfcs/inactive_users/user_and_organization_meta_update.md new file mode 100644 index 000000000..b30104076 --- /dev/null +++ b/rfcs/inactive_users/user_and_organization_meta_update.md @@ -0,0 +1,30 @@ +# User/Organization metadata update rework +## Overview and Motivation +When user or organization metadata needs to be updated, the Service uses the Redis pipeline javascript code. +For each assigned meta hash always exists a single `audience`, but there is no list of `audiences` assigned to the user or company. +To achieve easier audience tracking and a combined metadata update, I advise using a Lua based script. + +## Audience lists +Audiences stored in sets formed from `USERS_AUDIENCE` or `ORGANISATION_AUDIENCE` constants and `Id` +(eg: `{ms-users}10110110111!audiences`). Both keys contain `audience` names that are currently have assigned values. + +## DEL utils/updateMetadata.js +All logic from this file moved to separate class `utils/metadata/redis/update-metadata.js`. + +## utils/metadata/{user|organization}.js +Classes that perform user or organization metadata update and +audience list tracking. + +Available methods: + * update + * updateMulti + * batchUpdate + * delete + +**TODO** Fill up + + +## global updates +`ms-users` source code now use new `UserMetadata` when any update operation happens. + + diff --git a/src/actions/alias.js b/src/actions/alias.js index 9fb4855b0..15d030816 100644 --- a/src/actions/alias.js +++ b/src/actions/alias.js @@ -6,9 +6,10 @@ const isActive = require('../utils/is-active'); const isBanned = require('../utils/is-banned'); const key = require('../utils/key'); const handlePipeline = require('../utils/pipeline-error'); +const UserMetadata = require('../utils/metadata/user'); + const { USERS_DATA, - USERS_METADATA, USERS_ALIAS_TO_ID, USERS_ID_FIELD, USERS_ALIAS_FIELD, @@ -69,10 +70,11 @@ async function assignAlias({ params }) { return Promise.reject(err); } - const pipeline = redis.pipeline([ - ['hset', key(userId, USERS_DATA), USERS_ALIAS_FIELD, alias], - ['hset', key(userId, USERS_METADATA, defaultAudience), USERS_ALIAS_FIELD, JSON.stringify(alias)], - ]); + const pipeline = redis.pipeline(); + const userMetaData = new UserMetadata(pipeline); + + pipeline.hset(key(userId, USERS_DATA), USERS_ALIAS_FIELD, alias); + userMetaData.update(userId, USERS_ALIAS_FIELD, JSON.stringify(alias), defaultAudience); if (activeUser) { pipeline.sadd(USERS_PUBLIC_INDEX, username); diff --git a/src/actions/ban.js b/src/actions/ban.js index 91e8355ce..15c0c198e 100644 --- a/src/actions/ban.js +++ b/src/actions/ban.js @@ -4,9 +4,10 @@ const mapValues = require('lodash/mapValues'); const redisKey = require('../utils/key.js'); const { getInternalData } = require('../utils/userData'); const handlePipeline = require('../utils/pipeline-error'); +const UserMetadata = require('../utils/metadata/user'); + const { - USERS_DATA, USERS_METADATA, - USERS_BANNED_FLAG, USERS_TOKENS, USERS_BANNED_DATA, + USERS_DATA, USERS_BANNED_FLAG, USERS_TOKENS, USERS_BANNED_DATA, } = require('../constants.js'); // helper @@ -25,26 +26,30 @@ function lockUser({ remoteip: remoteip || '', }, }; + const pipeline = redis.pipeline(); + const userMetadata = new UserMetadata(pipeline); + + pipeline.hset(redisKey(id, USERS_DATA), USERS_BANNED_FLAG, 'true'); + // set .banned on metadata for filtering & sorting users by that field + userMetadata.updateMulti(id, mapValues(data, stringify), defaultAudience); + pipeline.del(redisKey(id, USERS_TOKENS)); - return redis - .pipeline() - .hset(redisKey(id, USERS_DATA), USERS_BANNED_FLAG, 'true') - // set .banned on metadata for filtering & sorting users by that field - .hmset(redisKey(id, USERS_METADATA, defaultAudience), mapValues(data, stringify)) - .del(redisKey(id, USERS_TOKENS)) - .exec(); + return pipeline.exec(); } function unlockUser({ id }) { const { redis, config } = this; const { jwt: { defaultAudience } } = config; + const pipeline = redis.pipeline(); + const userMetadata = new UserMetadata(pipeline); - return redis - .pipeline() - .hdel(redisKey(id, USERS_DATA), USERS_BANNED_FLAG) - // remove .banned on metadata for filtering & sorting users by that field - .hdel(redisKey(id, USERS_METADATA, defaultAudience), 'banned', USERS_BANNED_DATA) - .exec(); + pipeline.hdel(redisKey(id, USERS_DATA), USERS_BANNED_FLAG); + // remove .banned on metadata for filtering & sorting users by that field + userMetadata.delete(id, [ + 'banned', + USERS_BANNED_DATA, + ], defaultAudience); + return pipeline.exec(); } /** diff --git a/src/actions/organization/members/permission.js b/src/actions/organization/members/permission.js index 4e7e96eb7..00e094292 100644 --- a/src/actions/organization/members/permission.js +++ b/src/actions/organization/members/permission.js @@ -5,6 +5,7 @@ const { checkOrganizationExists } = require('../../../utils/organization'); const redisKey = require('../../../utils/key'); const handlePipeline = require('../../../utils/pipeline-error'); const getUserId = require('../../../utils/userData/get-user-id'); +const UserMetadata = require('../../../utils/metadata/user'); const { ErrorUserNotMember, USERS_METADATA, ORGANIZATIONS_MEMBERS } = require('../../../constants'); /** @@ -41,7 +42,9 @@ async function setOrganizationMemberPermission({ params }) { permissions = JSON.stringify(permissions); const pipeline = redis.pipeline(); - pipeline.hset(memberMetadataKey, organizationId, permissions); + const userMetadata = new UserMetadata(pipeline); + + userMetadata.update(userId, organizationId, permissions); pipeline.hset(redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId), 'permissions', permissions); return pipeline.exec().then(handlePipeline); diff --git a/src/actions/organization/members/remove.js b/src/actions/organization/members/remove.js index 61437cd59..142964b05 100644 --- a/src/actions/organization/members/remove.js +++ b/src/actions/organization/members/remove.js @@ -3,6 +3,7 @@ const redisKey = require('../../../utils/key'); const getUserId = require('../../../utils/userData/get-user-id'); const handlePipeline = require('../../../utils/pipeline-error'); const { checkOrganizationExists } = require('../../../utils/organization'); +const UserMetadata = require('../../../utils/metadata/user'); const { ORGANIZATIONS_MEMBERS, USERS_METADATA, @@ -34,9 +35,10 @@ async function removeMember({ params }) { } const pipeline = redis.pipeline(); + const userMetadata = new UserMetadata(pipeline); pipeline.del(memberKey); pipeline.zrem(redisKey(organizationId, ORGANIZATIONS_MEMBERS), memberKey); - pipeline.hdel(memberMetadataKey, organizationId); + userMetadata.delete(userId, organizationId, audience); return pipeline.exec().then(handlePipeline); } diff --git a/src/actions/register.js b/src/actions/register.js index e7f5347fc..a13ead175 100644 --- a/src/actions/register.js +++ b/src/actions/register.js @@ -9,7 +9,7 @@ const reduce = require('lodash/reduce'); const last = require('lodash/last'); // internal deps -const setMetadata = require('../utils/update-metadata'); +const UserMetadata = require('../utils/metadata/user'); const redisKey = require('../utils/key'); const jwt = require('../utils/jwt'); const isDisposable = require('../utils/is-disposable'); @@ -213,7 +213,7 @@ async function performRegistration({ service, params }) { await pipeline.exec().then(handlePipeline); - await setMetadata.call(service, { + await new UserMetadata(service.redis).batchUpdate({ userId, audience, metadata: audience.map((metaAudience) => ({ diff --git a/src/actions/updateMetadata.js b/src/actions/updateMetadata.js index e6ede60b7..024779d32 100644 --- a/src/actions/updateMetadata.js +++ b/src/actions/updateMetadata.js @@ -1,6 +1,6 @@ const omit = require('lodash/omit'); const Promise = require('bluebird'); -const updateMetadata = require('../utils/update-metadata'); +const UserMetadata = require('../utils/metadata/user'); const { getUserId } = require('../utils/userData'); /** @@ -19,12 +19,15 @@ const { getUserId } = require('../utils/userData'); * @apiParam (Payload) {Object} [script] - if present will be called with passed metadata keys & username, provides direct scripting access. * Be careful with granting access to this function. */ -module.exports = function updateMetadataAction(request) { - return Promise +module.exports = async function updateMetadataAction(request) { + const userId = await Promise .bind(this, request.params.username) - .then(getUserId) - .then((userId) => ({ ...omit(request.params, 'username'), userId })) - .then(updateMetadata); + .then(getUserId); + + const userMetadata = new UserMetadata(this.redis); + const updateParams = { ...omit(request.params, 'username'), userId }; + + return userMetadata.batchUpdate(updateParams); }; module.exports.transports = [require('@microfleet/core').ActionTransport.amqp]; diff --git a/src/auth/oauth/utils/attach.js b/src/auth/oauth/utils/attach.js index 6028ca7e1..88a2d8aba 100644 --- a/src/auth/oauth/utils/attach.js +++ b/src/auth/oauth/utils/attach.js @@ -1,13 +1,13 @@ const get = require('lodash/get'); const redisKey = require('../../../utils/key'); -const updateMetadata = require('../../../utils/update-metadata'); +const UserMetadata = require('../../../utils/metadata/user'); const handlePipeline = require('../../../utils/pipeline-error'); const { USERS_SSO_TO_ID, USERS_DATA, } = require('../../../constants'); -module.exports = function attach(account, user) { +module.exports = async function attach(account, user) { const { redis, config } = this; const { id: userId } = user; const { @@ -23,17 +23,19 @@ module.exports = function attach(account, user) { // link uid to user id pipeline.hset(USERS_SSO_TO_ID, uid, userId); - return pipeline.exec().then(handlePipeline) - .bind(this) - .return({ - userId, - audience, - metadata: { - $set: { - [provider]: profile, - }, + await pipeline.exec().then(handlePipeline); + + const userMetadata = new UserMetadata(redis); + const updateParams = { + userId, + audience, + metadata: { + $set: { + [provider]: profile, }, - }) - .then(updateMetadata) - .return(profile); + }, + }; + await userMetadata.batchUpdate(updateParams); + + return profile; }; diff --git a/src/auth/oauth/utils/detach.js b/src/auth/oauth/utils/detach.js index 8c4ea8530..3ddbbf3d4 100644 --- a/src/auth/oauth/utils/detach.js +++ b/src/auth/oauth/utils/detach.js @@ -2,7 +2,7 @@ const Errors = require('common-errors'); const get = require('../../../utils/get-value'); const redisKey = require('../../../utils/key'); -const updateMetadata = require('../../../utils/update-metadata'); +const UserMetadata = require('../../../utils/metadata/user'); const handlePipeline = require('../../../utils/pipeline-error'); const { @@ -10,7 +10,7 @@ const { USERS_DATA, } = require('../../../constants'); -module.exports = function detach(provider, userData) { +module.exports = async function detach(provider, userData) { const { id: userId } = userData; const { redis, config } = this; const audience = get(config, 'jwt.defaultAudience'); @@ -28,16 +28,18 @@ module.exports = function detach(provider, userData) { // delete account reference pipeline.hdel(USERS_SSO_TO_ID, uid); - return pipeline.exec().then(handlePipeline) - .bind(this) - .return({ - userId, - audience, - metadata: { - $remove: [ - provider, - ], - }, - }) - .then(updateMetadata); + await pipeline.exec().then(handlePipeline); + + const userMetadata = new UserMetadata(redis); + const updateParams = { + userId, + audience, + metadata: { + $remove: [ + provider, + ], + }, + }; + + return userMetadata.batchUpdate(updateParams); }; diff --git a/src/constants.js b/src/constants.js index 525284ecc..10f2033a2 100644 --- a/src/constants.js +++ b/src/constants.js @@ -18,6 +18,7 @@ module.exports = exports = { // hashes USERS_DATA: 'data', USERS_METADATA: 'metadata', + USERS_AUDIENCE: 'users-audiences', USERS_TOKENS: 'tokens', USERS_API_TOKENS: 'api-tokens', USERS_API_TOKENS_ZSET: 'api-tokens-set', @@ -26,6 +27,7 @@ module.exports = exports = { USERS_ORGANIZATIONS: 'user-organizations', ORGANIZATIONS_DATA: 'data', ORGANIZATIONS_METADATA: 'metadata', + ORGANIZATIONS_AUDIENCE: 'organization-audiences', ORGANIZATIONS_MEMBERS: 'members', // standard JWT with TTL diff --git a/src/custom/cappasity-users-activate.js b/src/custom/cappasity-users-activate.js index 94ac4faa4..0c1edb930 100644 --- a/src/custom/cappasity-users-activate.js +++ b/src/custom/cappasity-users-activate.js @@ -1,6 +1,6 @@ const find = require('lodash/find'); const moment = require('moment'); -const setMetadata = require('../utils/update-metadata'); +const UserMetadata = require('../utils/metadata/user'); /** * Adds metadata from billing into usermix @@ -13,6 +13,7 @@ module.exports = function mixPlan(userId, params) { const { payments } = config; const route = [payments.prefix, payments.routes.planGet].join('.'); const id = 'free'; + const userMetadata = new UserMetadata(this.redis); return amqp .publishAndWait(route, id, { timeout: 5000 }) @@ -20,7 +21,7 @@ module.exports = function mixPlan(userId, params) { .then(function mix(plan) { const subscription = find(plan.subs, ['name', 'month']); const nextCycle = moment().add(1, 'month').valueOf(); - const update = { + const updateParams = { userId, audience, metadata: { @@ -36,6 +37,6 @@ module.exports = function mixPlan(userId, params) { }, }; - return setMetadata.call(this, update); + return userMetadata.batchUpdate(updateParams); }); }; diff --git a/src/custom/rfx-create-room-on-activate.js b/src/custom/rfx-create-room-on-activate.js index 81bcfa779..c48f4d345 100644 --- a/src/custom/rfx-create-room-on-activate.js +++ b/src/custom/rfx-create-room-on-activate.js @@ -1,6 +1,6 @@ const is = require('is'); const Promise = require('bluebird'); -const setMetadata = require('../utils/update-metadata'); +const UserMetadata = require('../utils/metadata/user'); /** * @param {String} username @@ -22,10 +22,12 @@ function createRoom(userId, params, metadata) { name: `${metadata[audience].stationName} | ${metadata[audience].stationSchool}`, }; + const userMetadata = new UserMetadata(this.redis); + return amqp.publishAndWait(route, roomParams, { timeout: 5000 }) .bind(this) .then((room) => { - const update = { + const updateParams = { userId, audience, metadata: { @@ -35,7 +37,7 @@ function createRoom(userId, params, metadata) { }, }; - return setMetadata.call(this, update); + return userMetadata.batchUpdate(updateParams); }); } diff --git a/src/utils/metadata/organization.js b/src/utils/metadata/organization.js new file mode 100644 index 000000000..48ba54b01 --- /dev/null +++ b/src/utils/metadata/organization.js @@ -0,0 +1,27 @@ +const Promise = require('bluebird'); +const Audience = require('./redis/audience'); +const MetaUpdate = require('./redis/update-metadata'); +const { ORGANIZATIONS_METADATA, ORGANIZATIONS_AUDIENCE } = require('../../constants'); + +class Organization { + constructor(redis) { + this.redis = redis; + this.meta = new MetaUpdate(this.redis, ORGANIZATIONS_METADATA); + this.audience = new Audience(this.redis, ORGANIZATIONS_AUDIENCE); + } + + /** + * Updates metadata on a organization object + * @param {Object} opts + * @return {Promise} + */ + async batchUpdate(opts) { + const { organizationId, ...restOpts } = opts; + const audienceWork = this.audience.batchAdd(organizationId, restOpts.audience); + + await Promise.all(audienceWork); + return this.meta.batchUpdate({ id: organizationId, ...restOpts }); + } +} + +module.exports = Organization; diff --git a/src/utils/metadata/redis/audience.js b/src/utils/metadata/redis/audience.js new file mode 100644 index 000000000..c8a6da38d --- /dev/null +++ b/src/utils/metadata/redis/audience.js @@ -0,0 +1,30 @@ +class Audience { + constructor(redis, audienceKeyBase) { + this.redis = redis; + this.audienceKeyBase = audienceKeyBase; + } + + getAudienceKey(id) { + return `${id}!${this.audienceKeyBase}`; + } + + add(id, audience, redis = this.redis) { + return redis.sadd(this.getAudienceKey(id), audience); + } + + batchAdd(id, audiences, redis = this.redis) { + const audienceWork = []; + for (const audience of Array.isArray(audiences) ? audiences : [audiences]) { + audienceWork.push( + redis.sadd(this.getAudienceKey(id), audience) + ); + } + return audienceWork; + } + + delete(id, audience, redis = this.redis) { + return redis.srem(this.getAudienceKey(id), audience); + } +} + +module.exports = Audience; diff --git a/src/utils/metadata/redis/update-metadata.js b/src/utils/metadata/redis/update-metadata.js new file mode 100644 index 000000000..168181fb0 --- /dev/null +++ b/src/utils/metadata/redis/update-metadata.js @@ -0,0 +1,173 @@ +const Promise = require('bluebird'); +const mapValues = require('lodash/mapValues'); +const assert = require('assert'); +const { Pipeline } = require('ioredis'); +const { HttpStatusError } = require('common-errors'); +const handlePipeline = require('../../pipelineError'); +const sha256 = require('../../sha256'); + +const JSONStringify = (data) => JSON.stringify(data); + +/** + * Class wraps User/Organization metadata update using atomic LUA script + */ +class UpdateMetadata { + /** + * @param redis + * @param metadataKeyTemplate + * @param audienceKeyTemplate + */ + constructor(redis, metadataKeyBase) { + this.redis = redis; + this.metadataKeyBase = metadataKeyBase; + } + + getMetadataKey(id, audience) { + return `${id}!${this.metadataKeyBase}!${audience}`; + } + + update(id, audience, key, value, redis = this.redis) { + return redis.hset(this.getMetadataKey(id, audience), key, value); + } + + updateMulti(id, audience, values, redis = this.redis) { + return redis.hmset(this.getMetadataKey(id, audience), values); + } + + delete(id, audience, key, redis = this.redis) { + return redis.hdel(this.getMetadataKey(id, audience), key); + } + + /** + * Updates metadata on a user object + * @param {Object} opts + * @return {Promise} + */ + async batchUpdate(opts) { + const { redis } = this; + assert(!(redis instanceof Pipeline), 'impossible to use with pipeline'); + const { + id, audience, metadata, script, + } = opts; + const audiences = Array.isArray(audience) ? audience : [audience]; + + // keys + const keys = audiences.map((aud) => this.getMetadataKey(id, aud)); + + // if we have meta, then we can + if (metadata) { + const pipe = redis.pipeline(); + const metaOps = Array.isArray(metadata) ? metadata : [metadata]; + + if (metaOps.length !== audiences.length) { + return Promise.reject(new HttpStatusError(400, 'audiences must match metadata entries')); + } + + const operations = metaOps.map((meta, idx) => UpdateMetadata.handleAudience(pipe, keys[idx], meta)); + return pipe.exec() + .then(handlePipeline) + .then((res) => UpdateMetadata.mapMetaResponse(operations, res)); + } + + // dynamic scripts + const $scriptKeys = Object.keys(script); + const scripts = $scriptKeys.map((scriptName) => { + const { lua, argv = [] } = script[scriptName]; + const sha = sha256(lua); + const name = `ms_users_${sha}`; + if (typeof redis[name] !== 'function') { + redis.defineCommand(name, { lua }); + } + return redis[name](keys.length, keys, argv); + }); + + return Promise.all(scripts).then((res) => UpdateMetadata.mapScriptResponse($scriptKeys, res)); + } + + /** + * Process metadata update operation for a passed audience + * @param {Object} pipeline + * @param {String} audience + * @param {Object} metadata + */ + static handleAudience(pipeline, key, metadata) { + const { $remove } = metadata; + const $removeOps = $remove ? $remove.length : 0; + if ($removeOps > 0) { + pipeline.hdel(key, $remove); + } + + const { $set } = metadata; + const $setKeys = $set && Object.keys($set); + const $setLength = $setKeys ? $setKeys.length : 0; + if ($setLength > 0) { + pipeline.hmset(key, mapValues($set, JSONStringify)); + } + + const { $incr } = metadata; + const $incrFields = $incr && Object.keys($incr); + const $incrLength = $incrFields ? $incrFields.length : 0; + if ($incrLength > 0) { + $incrFields.forEach((fieldName) => { + pipeline.hincrby(key, fieldName, $incr[fieldName]); + }); + } + + return { + $removeOps, $setLength, $incrLength, $incrFields, + }; + } + + /** + * Maps updateMetadata ops + * @param {Array} responses + * @param {Array} operations + * @return {Object|Array} + */ + static mapMetaResponse(operations, responses) { + let cursor = 0; + return Promise + .map(operations, (props) => { + const { + $removeOps, $setLength, $incrLength, $incrFields, + } = props; + const output = {}; + + if ($removeOps > 0) { + output.$remove = responses[cursor]; + cursor += 1; + } + + if ($setLength > 0) { + output.$set = responses[cursor]; + cursor += 1; + } + + if ($incrLength > 0) { + const $incrResponse = output.$incr = {}; + $incrFields.forEach((fieldName) => { + $incrResponse[fieldName] = responses[cursor]; + cursor += 1; + }); + } + + return output; + }) + .then((ops) => (ops.length > 1 ? ops : ops[0])); + } + + /** + * Handle script, mutually exclusive with metadata + * @param {Array} scriptKeys + * @param {Array} responses + */ + static mapScriptResponse(scriptKeys, responses) { + const output = {}; + scriptKeys.forEach((fieldName, idx) => { + output[fieldName] = responses[idx]; + }); + return output; + } +} + +module.exports = UpdateMetadata; diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js new file mode 100644 index 000000000..fc106ba61 --- /dev/null +++ b/src/utils/metadata/user.js @@ -0,0 +1,58 @@ +const Promise = require('bluebird'); +const { Pipeline } = require('ioredis'); +const MetaUpdate = require('./redis/update-metadata'); +const Audience = require('./redis/audience'); + +const { USERS_METADATA, USERS_AUDIENCE } = require('../../constants'); + +class User { + constructor(redis) { + this.pipeline = redis instanceof Pipeline; + this.redis = redis; + this.metadata = new MetaUpdate(this.redis, USERS_METADATA); + this.audience = new Audience(this.redis, USERS_AUDIENCE); + } + + /** + * + * @param id - User id + * @param hashKey - Key in metadata + * @param value + * @param audience + * @returns {Promise} + */ + update(id, hashKey, value, audience) { + const work = [ + this.audience.add(id, audience), + this.metadata.update(id, audience, hashKey, value), + ]; + return this.pipeline ? Promise.all(work) : work; + } + + updateMulti(id, values, audience) { + const work = [ + this.audience.add(id, audience), + this.metadata.updateMulti(id, audience, values), + ]; + return this.pipeline ? Promise.all(work) : work; + } + + delete(id, hashKey, audience) { + return this.metadata.delete(id, audience, hashKey); + } + + /** + * Updates metadata on a user object using batch operations + * @param {Object} opts + * @return {Promise} + */ + async batchUpdate(opts) { + const { userId, ...restOpts } = opts; + const audienceWork = this.audience.batchAdd(userId, restOpts.audience); + + await Promise.all(audienceWork); + return this.metadata.batchUpdate({ id: userId, ...restOpts }); + } +} + +module.exports = User; diff --git a/src/utils/organization/add-organization-members.js b/src/utils/organization/add-organization-members.js index fa98eb300..5e397b079 100644 --- a/src/utils/organization/add-organization-members.js +++ b/src/utils/organization/add-organization-members.js @@ -7,9 +7,10 @@ const sendInviteMail = require('./send-invite-email'); const getInternalData = require('./get-internal-data'); const registerOrganizationMembers = require('./register-organization-members'); const handlePipeline = require('../pipeline-error'); +const UserMetadata = require('../metadata/user'); + const { ORGANIZATIONS_MEMBERS, - USERS_METADATA, ORGANIZATIONS_NAME_FIELD, USERS_ACTION_ORGANIZATION_INVITE, USERS_ACTION_ORGANIZATION_REGISTER, @@ -42,18 +43,18 @@ async function addOrganizationMembers(opts) { const createdMembers = await registerOrganizationMembers.call(this, notRegisteredMembers); const pipe = redis.pipeline(); + const userMetadata = new UserMetadata(pipe); const membersKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS); const organizationMembers = registeredMembers.concat(createdMembers); organizationMembers.forEach(({ password, ...member }) => { const memberKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS, member.id); - const memberOrganizations = redisKey(member.id, USERS_METADATA, audience); member.username = member.email; member.invited = Date.now(); member.accepted = password ? Date.now() : null; member.permissions = member.permissions || []; const stringifyMember = mapValues(member, JSONStringify); pipe.hmset(memberKey, stringifyMember); - pipe.hset(memberOrganizations, organizationId, stringifyMember.permissions); + userMetadata.update(member.id, organizationId, stringifyMember.permissions, audience); pipe.zadd(membersKey, stringifyMember.invited, memberKey); }); diff --git a/src/utils/organization/register-organization-members.js b/src/utils/organization/register-organization-members.js index 788a08e7f..0006cc061 100644 --- a/src/utils/organization/register-organization-members.js +++ b/src/utils/organization/register-organization-members.js @@ -14,7 +14,7 @@ const { USERS_ID_FIELD, } = require('../../constants.js'); const scrypt = require('../scrypt'); -const setMetadata = require('../update-metadata'); +const UserMetadata = require('../metadata/user'); async function registerOrganizationMember(member) { const { redis, config } = this; @@ -36,7 +36,8 @@ async function registerOrganizationMember(member) { pipeline.hset(USERS_USERNAME_TO_ID, email, userId); await pipeline.exec().then(handlePipeline); - await setMetadata.call(this, { + const userMetadata = new UserMetadata(redis); + await userMetadata.batchUpdate({ userId, audience, metadata: [{ diff --git a/src/utils/set-organization-metadata.js b/src/utils/set-organization-metadata.js index fce6659cf..6480c9417 100644 --- a/src/utils/set-organization-metadata.js +++ b/src/utils/set-organization-metadata.js @@ -1,11 +1,5 @@ /* eslint-disable no-mixed-operators */ -const Promise = require('bluebird'); -const is = require('is'); -const { HttpStatusError } = require('common-errors'); -const redisKey = require('../utils/key'); -const handlePipeline = require('../utils/pipeline-error'); -const { handleAudience } = require('../utils/update-metadata'); -const { ORGANIZATIONS_METADATA } = require('../constants'); +const OrganizationMetadata = require('../utils/metadata/organization'); /** * Updates metadata on a organization object @@ -13,29 +7,7 @@ const { ORGANIZATIONS_METADATA } = require('../constants'); * @return {Promise} */ async function setOrganizationMetadata(opts) { - const { redis } = this; - const { - organizationId, audience, metadata, - } = opts; - const audiences = is.array(audience) ? audience : [audience]; - - // keys - const keys = audiences.map((aud) => redisKey(organizationId, ORGANIZATIONS_METADATA, aud)); - - // if we have meta, then we can - if (metadata) { - const pipe = redis.pipeline(); - const metaOps = is.array(metadata) ? metadata : [metadata]; - - if (metaOps.length !== audiences.length) { - return Promise.reject(new HttpStatusError(400, 'audiences must match metadata entries')); - } - - metaOps.forEach((meta, idx) => handleAudience(pipe, keys[idx], meta)); - return pipe.exec().then(handlePipeline); - } - - return true; + return new OrganizationMetadata(this.redis).batchUpdate(opts); } module.exports = setOrganizationMetadata; diff --git a/test/suites/ban.js b/test/suites/ban.js index 3ab61b30f..24bb863b5 100644 --- a/test/suites/ban.js +++ b/test/suites/ban.js @@ -34,9 +34,8 @@ describe('#ban', function banSuite() { it('must be able to ban an existing user', async function test() { const response = await this.dispatch('users.ban', { username, ban: true }); - assert.equal(response[0], 1); - assert.equal(response[1], 'OK'); + assert.equal(response[2], 'OK'); }); it('requesting metadata with a special flag verifies ban state and throws', async function test() { diff --git a/test/suites/update-metadata.js b/test/suites/update-metadata.js index b03ba7492..d312e23d1 100644 --- a/test/suites/update-metadata.js +++ b/test/suites/update-metadata.js @@ -64,6 +64,7 @@ describe('#updateMetadata', function getMetadataSuite() { $incr: { b: 2, }, + $remove: ['c'], }, { $incr: { @@ -75,15 +76,13 @@ describe('#updateMetadata', function getMetadataSuite() { .then(inspectPromise()) .then((data) => { const [mainData, extraData] = data; - expect(mainData.$set).to.be.eq('OK'); expect(mainData.$incr.b).to.be.eq(2); expect(extraData.$incr.b).to.be.eq(3); }); }); - it('must be able to run dynamic scripts', function test() { - const dispatch = simpleDispatcher(this.users.router); + it('must be able to run dynamic scripts', async function test() { const params = { username, audience: [audience, extra], @@ -95,15 +94,68 @@ describe('#updateMetadata', function getMetadataSuite() { }, }; - return dispatch('users.updateMetadata', params) - .reflect() - .then(inspectPromise()) - .then((data) => { - expect(data.balance).to.be.deep.eq([ - `{ms-users}${this.userId}!metadata!${audience}`, - `{ms-users}${this.userId}!metadata!${extra}`, - 'nom-nom', - ]); - }); + const updated = await this.dispatch('users.updateMetadata', params); + + expect(updated.balance).to.be.deep.eq([ + `{ms-users}${this.userId}!metadata!${audience}`, + `{ms-users}${this.userId}!metadata!${extra}`, + 'nom-nom', + ]); + }); + + it('tracks audienceList', async function test() { + const params = { + username, + audience: [ + audience, + '*.extra', + ], + metadata: [ + { + $set: { + x: 10, + b: 12, + c: 'cval', + }, + }, { + $set: { + x: 20, + b: 22, + c: 'xval', + }, + }, + ], + }; + + await this.dispatch('users.updateMetadata', params); + const audiencesList = await this.users.redis.smembers(`${this.userId}!users-audiences`); + expect(audiencesList).to.include.members(['*.localhost', '*.extra']); + }); + + it('must be able to run dynamic scripts / default namespace available', async function test() { + const lua = ` + local t = {} + table.insert(t, "foo") + local jsonDec = cjson.decode('{"bar": 1}') + local typeCheck = type(t) + redis.call("SET", "fookey", 777); + return {jsonDec.bar, redis.call("TIME"), redis.call("GET", "fookey"), typeCheck, unpack(t)} + `; + + const params = { + username, + audience: [audience], + script: { + check: { + lua, + argv: ['nom-nom'], + }, + }, + }; + const updated = await this.dispatch('users.updateMetadata', params); + const [jsonVal, redisTime, keyValue] = updated.check; + expect(jsonVal).to.be.eq(1); + expect(redisTime).to.be.an('array'); + expect(keyValue).to.be.eq('777'); }); }); From ca15da540ec833d5b833efdbb9917e83852b4d22 Mon Sep 17 00:00:00 2001 From: pajgo Date: Thu, 31 Oct 2019 18:14:41 +0600 Subject: [PATCH 04/13] feat: update user and organization metadata * asserts and rf update --- .../user_and_organization_meta_update.md | 38 ++++----- src/actions/organization/delete.js | 5 ++ src/actions/remove.js | 4 + src/utils/asserts/id.js | 3 + src/utils/asserts/integer.js | 3 + src/utils/asserts/redis.js | 14 ++++ src/utils/asserts/string-not-empty.js | 3 + src/utils/metadata/organization.js | 15 ++-- src/utils/metadata/redis/audience.js | 49 +++++++++--- src/utils/metadata/redis/update-metadata.js | 78 +++++++++++++++---- src/utils/metadata/user.js | 39 +++++++--- 11 files changed, 187 insertions(+), 64 deletions(-) create mode 100644 src/utils/asserts/id.js create mode 100644 src/utils/asserts/integer.js create mode 100644 src/utils/asserts/redis.js create mode 100644 src/utils/asserts/string-not-empty.js diff --git a/rfcs/inactive_users/user_and_organization_meta_update.md b/rfcs/inactive_users/user_and_organization_meta_update.md index b30104076..f94d20ba0 100644 --- a/rfcs/inactive_users/user_and_organization_meta_update.md +++ b/rfcs/inactive_users/user_and_organization_meta_update.md @@ -1,30 +1,24 @@ -# User/Organization metadata update rework +# User/Organization metadata update ## Overview and Motivation -When user or organization metadata needs to be updated, the Service uses the Redis pipeline javascript code. -For each assigned meta hash always exists a single `audience`, but there is no list of `audiences` assigned to the user or company. -To achieve easier audience tracking and a combined metadata update, I advise using a Lua based script. +When user or organization metadata is updated, the Service should track audiences with assigned metadata. +For each assigned meta hash always exists a single `audience`, but there is no list of `audiences` assigned to the user or organization. -## Audience lists -Audiences stored in sets formed from `USERS_AUDIENCE` or `ORGANISATION_AUDIENCE` constants and `Id` -(eg: `{ms-users}10110110111!audiences`). Both keys contain `audience` names that are currently have assigned values. - -## DEL utils/updateMetadata.js -All logic from this file moved to separate class `utils/metadata/redis/update-metadata.js`. +To achieve this ability, I advise these updates: -## utils/metadata/{user|organization}.js -Classes that perform user or organization metadata update and -audience list tracking. - -Available methods: - * update - * updateMulti - * batchUpdate - * delete +## Audience lists +Audiences stored in sets with names created from `USERS_AUDIENCE` or `ORGANISATION_AUDIENCE` constants and `Id` +(e.g.: `{ms-users}10110110111!audiences`). Both keys contain `audience` names that are currently have assigned values. -**TODO** Fill up +The `audience` list will be updated on each update of the metadata. +## Metadata Handling classes +Service logic is updated to use 2 specific classes that will perform all CRUD operations on User or Organization metadata. -## global updates -`ms-users` source code now use new `UserMetadata` when any update operation happens. +* Classes located in: `utils/metadata/{user|organization}.js`. +* Both classes use same [Redis backend](#redis-metadata-backend-class). +## Redis Metadata Backend class +The class performs all work on metadata using Redis DB as a backend. +## Notice +* All User or Organization metadata operations should be performed using Provided classes otherwise, audiences won't be tracked. diff --git a/src/actions/organization/delete.js b/src/actions/organization/delete.js index 76065b4bf..f4e087da0 100644 --- a/src/actions/organization/delete.js +++ b/src/actions/organization/delete.js @@ -3,6 +3,7 @@ const snakeCase = require('lodash/snakeCase'); const redisKey = require('../../utils/key'); const handlePipeline = require('../../utils/pipeline-error'); const { checkOrganizationExists, getInternalData } = require('../../utils/organization'); +const OrganizationMetadata = require('../../utils/metadata/organization'); const { ORGANIZATIONS_DATA, ORGANIZATIONS_METADATA, @@ -32,11 +33,15 @@ async function deleteOrganization({ params }) { const organizationMembersListKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS); const organizationMembersIds = await redis.zrange(organizationMembersListKey, 0, -1); const organization = await getInternalData.call(this, organizationId); + const organizationMetadata = new OrganizationMetadata(redis); const pipeline = redis.pipeline(); pipeline.del(redisKey(organizationId, ORGANIZATIONS_DATA)); pipeline.del(redisKey(organizationId, ORGANIZATIONS_METADATA, audience)); + // delete organization audiences index + pipeline.del(organizationMetadata.audience.getAudienceKey(organizationId)); + pipeline.srem(ORGANIZATIONS_INDEX, organizationId); if (organizationMembersIds) { organizationMembersIds.forEach((memberId) => { diff --git a/src/actions/remove.js b/src/actions/remove.js index 0951c03bf..f89f87ba2 100644 --- a/src/actions/remove.js +++ b/src/actions/remove.js @@ -7,6 +7,7 @@ const key = require('../utils/key'); const { getInternalData } = require('../utils/userData'); const getMetadata = require('../utils/get-metadata'); const handlePipeline = require('../utils/pipeline-error'); +const UserMetadata = require('../utils/metadata/user'); const { USERS_INDEX, USERS_PUBLIC_INDEX, @@ -69,6 +70,7 @@ async function removeUser({ params }) { } const transaction = redis.pipeline(); + const userMetadata = new UserMetadata(transaction); const alias = internal[USERS_ALIAS_FIELD]; const userId = internal[USERS_ID_FIELD]; const resolvedUsername = internal[USERS_USERNAME_FIELD]; @@ -94,7 +96,9 @@ async function removeUser({ params }) { // remove metadata & internal data transaction.del(key(userId, USERS_DATA)); + // TODO fix if user has multiple audiences some data will remain transaction.del(key(userId, USERS_METADATA, audience)); + transaction.del(userMetadata.audience.getAudienceKey(userId)); // remove auth tokens transaction.del(key(userId, USERS_TOKENS)); diff --git a/src/utils/asserts/id.js b/src/utils/asserts/id.js new file mode 100644 index 000000000..5ea248bee --- /dev/null +++ b/src/utils/asserts/id.js @@ -0,0 +1,3 @@ +module.exports = function isId(value) { + return Number.isInteger(value) || (typeof value === 'string' && value.length > 0); +}; diff --git a/src/utils/asserts/integer.js b/src/utils/asserts/integer.js new file mode 100644 index 000000000..56b6477c6 --- /dev/null +++ b/src/utils/asserts/integer.js @@ -0,0 +1,3 @@ +module.exports = function isInteger(value) { + return Number.isInteger(value); +}; diff --git a/src/utils/asserts/redis.js b/src/utils/asserts/redis.js new file mode 100644 index 000000000..2dd184513 --- /dev/null +++ b/src/utils/asserts/redis.js @@ -0,0 +1,14 @@ +const Redis = require('ioredis'); + +function isRedisPipeline(value) { + return value instanceof Redis.Pipeline; +} + +function isRedis(value) { + return value instanceof Redis || value instanceof Redis.Cluster || value instanceof Redis.Pipeline; +} + +module.exports = { + isRedisPipeline, + isRedis, +}; diff --git a/src/utils/asserts/string-not-empty.js b/src/utils/asserts/string-not-empty.js new file mode 100644 index 000000000..d6a2af0ba --- /dev/null +++ b/src/utils/asserts/string-not-empty.js @@ -0,0 +1,3 @@ +module.exports = function stringNotEmpty(value) { + return typeof value === 'string' && value.length !== 0; +}; diff --git a/src/utils/metadata/organization.js b/src/utils/metadata/organization.js index 48ba54b01..df8af93fd 100644 --- a/src/utils/metadata/organization.js +++ b/src/utils/metadata/organization.js @@ -1,9 +1,14 @@ -const Promise = require('bluebird'); const Audience = require('./redis/audience'); const MetaUpdate = require('./redis/update-metadata'); const { ORGANIZATIONS_METADATA, ORGANIZATIONS_AUDIENCE } = require('../../constants'); -class Organization { +/** + * Class handling Organization Metadata operations + */ +class OrganizationMetadata { + /** + * @param {ioredis|Pipeline} redis + */ constructor(redis) { this.redis = redis; this.meta = new MetaUpdate(this.redis, ORGANIZATIONS_METADATA); @@ -17,11 +22,9 @@ class Organization { */ async batchUpdate(opts) { const { organizationId, ...restOpts } = opts; - const audienceWork = this.audience.batchAdd(organizationId, restOpts.audience); - - await Promise.all(audienceWork); + await this.audience.add(organizationId, restOpts.audience); return this.meta.batchUpdate({ id: organizationId, ...restOpts }); } } -module.exports = Organization; +module.exports = OrganizationMetadata; diff --git a/src/utils/metadata/redis/audience.js b/src/utils/metadata/redis/audience.js index c8a6da38d..395a236bc 100644 --- a/src/utils/metadata/redis/audience.js +++ b/src/utils/metadata/redis/audience.js @@ -1,28 +1,57 @@ +const assert = require('assert'); +const { isRedis } = require('../../asserts/redis'); +const isNotEmptyString = require('../../asserts/string-not-empty'); +const isValidId = require('../../asserts/id'); + +/** + * Class handling Audience tracking using Redis backend + */ class Audience { + /** + * @param {ioredis|Pipeline} redis + * @param {string} audienceKeyBase + */ constructor(redis, audienceKeyBase) { + assert(isRedis(redis), 'must be ioredis instance'); + assert(isNotEmptyString(audienceKeyBase), 'must be not empty string'); this.redis = redis; this.audienceKeyBase = audienceKeyBase; } + /** + * Generates Redis key + * Template `{id}!{metadataKeyBase}` + * @param {String|Number} id + * @returns {string} + */ getAudienceKey(id) { + assert(isValidId(id), 'must be valid Id'); return `${id}!${this.audienceKeyBase}`; } + /** + * Adds audience + * @param {String|Number} id + * @param {String|Array} audience + * @param {ioredis|Pipeline} [redis] + * @returns {Promise|Pipeline} + */ add(id, audience, redis = this.redis) { + assert(isRedis(redis), 'must be ioredis instance'); + assert(isNotEmptyString(audience) || Array.isArray(audience), 'must be not empty string or Array'); return redis.sadd(this.getAudienceKey(id), audience); } - batchAdd(id, audiences, redis = this.redis) { - const audienceWork = []; - for (const audience of Array.isArray(audiences) ? audiences : [audiences]) { - audienceWork.push( - redis.sadd(this.getAudienceKey(id), audience) - ); - } - return audienceWork; - } - + /** + * Deletes audience + * @param {String|Number} id + * @param {String} audience + * @param {ioredis|Pipeline} [redis] + * @returns {Promise|Pipeline} + */ delete(id, audience, redis = this.redis) { + assert(isRedis(redis), 'must be ioredis instance'); + assert(isNotEmptyString(audience) || Array.isArray(audience), 'must be not empty string or Array'); return redis.srem(this.getAudienceKey(id), audience); } } diff --git a/src/utils/metadata/redis/update-metadata.js b/src/utils/metadata/redis/update-metadata.js index 168181fb0..41d82bfbd 100644 --- a/src/utils/metadata/redis/update-metadata.js +++ b/src/utils/metadata/redis/update-metadata.js @@ -1,57 +1,103 @@ const Promise = require('bluebird'); const mapValues = require('lodash/mapValues'); const assert = require('assert'); -const { Pipeline } = require('ioredis'); const { HttpStatusError } = require('common-errors'); -const handlePipeline = require('../../pipelineError'); +const handlePipeline = require('../../pipeline-error'); const sha256 = require('../../sha256'); +const { isRedis, isRedisPipeline } = require('../../asserts/redis'); +const isNotEmptyString = require('../../asserts/string-not-empty'); +const isValidId = require('../../asserts/id'); const JSONStringify = (data) => JSON.stringify(data); /** - * Class wraps User/Organization metadata update using atomic LUA script + * Class handling metadata update operations using Redis backend */ class UpdateMetadata { /** - * @param redis - * @param metadataKeyTemplate - * @param audienceKeyTemplate + * @param {ioredis} redis or pipeline instance + * @param metadataKeyTemplate template base for Metadata key */ constructor(redis, metadataKeyBase) { + assert(isRedis(redis), 'must be ioredis instance'); + assert(isNotEmptyString(metadataKeyBase), 'must be not empty string'); this.redis = redis; this.metadataKeyBase = metadataKeyBase; } + /** + * Generates Redis key + * Template `{id}!{metadataKeyBase}!{audience}` + * @param {String|integer} id + * @param {String} audience + * @returns {String} + */ getMetadataKey(id, audience) { + assert(isValidId(id), 'must be valid Id'); + assert(isNotEmptyString(audience), 'must be not empty string'); return `${id}!${this.metadataKeyBase}!${audience}`; } + /** + * Updates metadata hash key + * @param {String} id + * @param {String} audience + * @param {String} key - Hash key + * @param {*} value + * @param {ioredis} [redis] + * @returns {Promise|Pipeline} + */ update(id, audience, key, value, redis = this.redis) { + assert(isNotEmptyString(key), 'must be not empty string'); + assert(isRedis(redis), 'must be ioredis instance'); + assert(value, 'must not be empty'); + return redis.hset(this.getMetadataKey(id, audience), key, value); } + /** + * Updates metadata hash keys + * @param {String} id + * @param {String} audience + * @param {Object} values - Object with keys and values + * @param {ioredis} [redis] + * @returns {Promise|Pipeline} + */ updateMulti(id, audience, values, redis = this.redis) { + assert(values !== null && typeof values === 'object', 'must be an object'); return redis.hmset(this.getMetadataKey(id, audience), values); } + /** + * Deletes metadata hash keys + * @param {String} id + * @param {String} audience + * @param {String} key - Hash key + * @param {ioredis} [redis] + * @returns {Promise|Pipeline} + */ delete(id, audience, key, redis = this.redis) { + assert(isNotEmptyString(key) || Array.isArray(key), 'must be not empty string or Array'); + assert(isRedis(redis), 'must be ioredis instance'); return redis.hdel(this.getMetadataKey(id, audience), key); } /** - * Updates metadata on a user object + * Updates metadata hash on provided Id * @param {Object} opts - * @return {Promise} + * @return {*} */ async batchUpdate(opts) { const { redis } = this; - assert(!(redis instanceof Pipeline), 'impossible to use with pipeline'); const { id, audience, metadata, script, } = opts; - const audiences = Array.isArray(audience) ? audience : [audience]; - // keys + // we use own pipeline or Promise here + assert(!isRedisPipeline(redis), 'impossible to use with pipeline'); + assert(isValidId(id), 'must be valid id'); + + const audiences = Array.isArray(audience) ? audience : [audience]; const keys = audiences.map((aud) => this.getMetadataKey(id, aud)); // if we have meta, then we can @@ -64,9 +110,9 @@ class UpdateMetadata { } const operations = metaOps.map((meta, idx) => UpdateMetadata.handleAudience(pipe, keys[idx], meta)); - return pipe.exec() - .then(handlePipeline) - .then((res) => UpdateMetadata.mapMetaResponse(operations, res)); + const result = handlePipeline(await pipe.exec()); + + return UpdateMetadata.mapMetaResponse(operations, result); } // dynamic scripts @@ -81,7 +127,9 @@ class UpdateMetadata { return redis[name](keys.length, keys, argv); }); - return Promise.all(scripts).then((res) => UpdateMetadata.mapScriptResponse($scriptKeys, res)); + const result = await Promise.all(scripts); + + return UpdateMetadata.mapScriptResponse($scriptKeys, result); } /** diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js index fc106ba61..074bef656 100644 --- a/src/utils/metadata/user.js +++ b/src/utils/metadata/user.js @@ -5,7 +5,13 @@ const Audience = require('./redis/audience'); const { USERS_METADATA, USERS_AUDIENCE } = require('../../constants'); -class User { +/** + * Class handles User metadata operations + */ +class UserMetadata { + /** + * @param {ioredis|Pipeline} redis + */ constructor(redis) { this.pipeline = redis instanceof Pipeline; this.redis = redis; @@ -14,12 +20,11 @@ class User { } /** - * - * @param id - User id - * @param hashKey - Key in metadata - * @param value - * @param audience - * @returns {Promise} + * Updates metadata field on a user object + * @param {String|Number} id + * @param {Object} values + * @param {String} audience + * @returns {Promise|void} */ update(id, hashKey, value, audience) { const work = [ @@ -29,6 +34,13 @@ class User { return this.pipeline ? Promise.all(work) : work; } + /** + * Updates metadata on a user object using fields and values from provided Object + * @param {String|Number} id + * @param {Object} values + * @param {String} audience + * @returns {Promise|void} + */ updateMulti(id, values, audience) { const work = [ this.audience.add(id, audience), @@ -37,6 +49,13 @@ class User { return this.pipeline ? Promise.all(work) : work; } + /** + * Deletes key from user metadata object + * @param {String|Number} id + * @param {String} hashKey + * @param {String} audience + * @returns {Promise|void} + */ delete(id, hashKey, audience) { return this.metadata.delete(id, audience, hashKey); } @@ -48,11 +67,9 @@ class User { */ async batchUpdate(opts) { const { userId, ...restOpts } = opts; - const audienceWork = this.audience.batchAdd(userId, restOpts.audience); - - await Promise.all(audienceWork); + await this.audience.add(userId, restOpts.audience); return this.metadata.batchUpdate({ id: userId, ...restOpts }); } } -module.exports = User; +module.exports = UserMetadata; From db3902caf67bdf56371a3aacb5f382ab43f84915 Mon Sep 17 00:00:00 2001 From: pajgo Date: Thu, 31 Oct 2019 19:00:32 +0600 Subject: [PATCH 05/13] feat: update user and organization metadata * typo fix --- src/actions/organization/members/permission.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/organization/members/permission.js b/src/actions/organization/members/permission.js index 00e094292..59ce7362d 100644 --- a/src/actions/organization/members/permission.js +++ b/src/actions/organization/members/permission.js @@ -44,7 +44,7 @@ async function setOrganizationMemberPermission({ params }) { const pipeline = redis.pipeline(); const userMetadata = new UserMetadata(pipeline); - userMetadata.update(userId, organizationId, permissions); + userMetadata.update(userId, organizationId, permissions, audience); pipeline.hset(redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId), 'permissions', permissions); return pipeline.exec().then(handlePipeline); From 42f609b1bac276dc7a86594448284fb8a55020da Mon Sep 17 00:00:00 2001 From: pajgo Date: Fri, 1 Nov 2019 15:12:22 +0600 Subject: [PATCH 06/13] feat: update user and organization metadata * slight api changes --- src/actions/alias.js | 5 +- src/actions/ban.js | 16 +- .../organization/members/permission.js | 5 +- src/actions/organization/members/remove.js | 5 +- src/actions/register.js | 23 ++- src/actions/remove.js | 2 +- src/actions/updateMetadata.js | 9 +- src/auth/oauth/utils/attach.js | 7 +- src/auth/oauth/utils/detach.js | 7 +- src/custom/cappasity-users-activate.js | 7 +- src/custom/rfx-create-room-on-activate.js | 6 +- src/utils/metadata/organization.js | 15 +- src/utils/metadata/redis/audience.js | 24 +++ src/utils/metadata/redis/update-metadata.js | 2 +- src/utils/metadata/user.js | 66 +++++--- .../organization/add-organization-members.js | 5 +- .../register-organization-members.js | 23 ++- src/utils/update-metadata.js | 144 ------------------ test/suites/update-metadata.js | 94 ++++++------ 19 files changed, 188 insertions(+), 277 deletions(-) delete mode 100644 src/utils/update-metadata.js diff --git a/src/actions/alias.js b/src/actions/alias.js index 15d030816..0393e9624 100644 --- a/src/actions/alias.js +++ b/src/actions/alias.js @@ -71,10 +71,11 @@ async function assignAlias({ params }) { } const pipeline = redis.pipeline(); - const userMetaData = new UserMetadata(pipeline); pipeline.hset(key(userId, USERS_DATA), USERS_ALIAS_FIELD, alias); - userMetaData.update(userId, USERS_ALIAS_FIELD, JSON.stringify(alias), defaultAudience); + UserMetadata + .for(userId, defaultAudience, pipeline) + .update(USERS_ALIAS_FIELD, JSON.stringify(alias)); if (activeUser) { pipeline.sadd(USERS_PUBLIC_INDEX, username); diff --git a/src/actions/ban.js b/src/actions/ban.js index 15c0c198e..d9ce7d207 100644 --- a/src/actions/ban.js +++ b/src/actions/ban.js @@ -27,11 +27,12 @@ function lockUser({ }, }; const pipeline = redis.pipeline(); - const userMetadata = new UserMetadata(pipeline); pipeline.hset(redisKey(id, USERS_DATA), USERS_BANNED_FLAG, 'true'); // set .banned on metadata for filtering & sorting users by that field - userMetadata.updateMulti(id, mapValues(data, stringify), defaultAudience); + UserMetadata + .for(id, defaultAudience, pipeline) + .updateMulti(mapValues(data, stringify)); pipeline.del(redisKey(id, USERS_TOKENS)); return pipeline.exec(); @@ -41,14 +42,15 @@ function unlockUser({ id }) { const { redis, config } = this; const { jwt: { defaultAudience } } = config; const pipeline = redis.pipeline(); - const userMetadata = new UserMetadata(pipeline); pipeline.hdel(redisKey(id, USERS_DATA), USERS_BANNED_FLAG); // remove .banned on metadata for filtering & sorting users by that field - userMetadata.delete(id, [ - 'banned', - USERS_BANNED_DATA, - ], defaultAudience); + UserMetadata + .for(id, defaultAudience, pipeline) + .delete([ + 'banned', + USERS_BANNED_DATA, + ]); return pipeline.exec(); } diff --git a/src/actions/organization/members/permission.js b/src/actions/organization/members/permission.js index 59ce7362d..ef8cb0169 100644 --- a/src/actions/organization/members/permission.js +++ b/src/actions/organization/members/permission.js @@ -42,9 +42,10 @@ async function setOrganizationMemberPermission({ params }) { permissions = JSON.stringify(permissions); const pipeline = redis.pipeline(); - const userMetadata = new UserMetadata(pipeline); - userMetadata.update(userId, organizationId, permissions, audience); + UserMetadata + .for(userId, audience, pipeline) + .update(organizationId, permissions); pipeline.hset(redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId), 'permissions', permissions); return pipeline.exec().then(handlePipeline); diff --git a/src/actions/organization/members/remove.js b/src/actions/organization/members/remove.js index 142964b05..26d0381cb 100644 --- a/src/actions/organization/members/remove.js +++ b/src/actions/organization/members/remove.js @@ -35,10 +35,11 @@ async function removeMember({ params }) { } const pipeline = redis.pipeline(); - const userMetadata = new UserMetadata(pipeline); pipeline.del(memberKey); pipeline.zrem(redisKey(organizationId, ORGANIZATIONS_MEMBERS), memberKey); - userMetadata.delete(userId, organizationId, audience); + UserMetadata + .for(userId, audience, pipeline) + .delete(organizationId); return pipeline.exec().then(handlePipeline); } diff --git a/src/actions/register.js b/src/actions/register.js index a13ead175..c31efd0e8 100644 --- a/src/actions/register.js +++ b/src/actions/register.js @@ -212,18 +212,17 @@ async function performRegistration({ service, params }) { } await pipeline.exec().then(handlePipeline); - - await new UserMetadata(service.redis).batchUpdate({ - userId, - audience, - metadata: audience.map((metaAudience) => ({ - $set: Object.assign(metadata[metaAudience] || {}, metaAudience === defaultAudience && { - [USERS_ID_FIELD]: userId, - [USERS_USERNAME_FIELD]: username, - [USERS_CREATED_FIELD]: created, - }), - })), - }); + await UserMetadata + .for(userId, audience, service.redis) + .batchUpdate({ + metadata: audience.map((metaAudience) => ({ + $set: Object.assign(metadata[metaAudience] || {}, metaAudience === defaultAudience && { + [USERS_ID_FIELD]: userId, + [USERS_USERNAME_FIELD]: username, + [USERS_CREATED_FIELD]: created, + }), + })), + }); // assign alias if (alias) { diff --git a/src/actions/remove.js b/src/actions/remove.js index f89f87ba2..0cae563b6 100644 --- a/src/actions/remove.js +++ b/src/actions/remove.js @@ -70,7 +70,7 @@ async function removeUser({ params }) { } const transaction = redis.pipeline(); - const userMetadata = new UserMetadata(transaction); + const userMetadata = new UserMetadata(redis); const alias = internal[USERS_ALIAS_FIELD]; const userId = internal[USERS_ID_FIELD]; const resolvedUsername = internal[USERS_USERNAME_FIELD]; diff --git a/src/actions/updateMetadata.js b/src/actions/updateMetadata.js index 024779d32..a4a1b5e1b 100644 --- a/src/actions/updateMetadata.js +++ b/src/actions/updateMetadata.js @@ -1,4 +1,3 @@ -const omit = require('lodash/omit'); const Promise = require('bluebird'); const UserMetadata = require('../utils/metadata/user'); const { getUserId } = require('../utils/userData'); @@ -20,14 +19,14 @@ const { getUserId } = require('../utils/userData'); * Be careful with granting access to this function. */ module.exports = async function updateMetadataAction(request) { + const { username: _, audience, ...updateParams } = request.params; const userId = await Promise .bind(this, request.params.username) .then(getUserId); - const userMetadata = new UserMetadata(this.redis); - const updateParams = { ...omit(request.params, 'username'), userId }; - - return userMetadata.batchUpdate(updateParams); + return UserMetadata + .for(userId, audience, this.redis) + .batchUpdate(updateParams); }; module.exports.transports = [require('@microfleet/core').ActionTransport.amqp]; diff --git a/src/auth/oauth/utils/attach.js b/src/auth/oauth/utils/attach.js index 88a2d8aba..ebd90c7d4 100644 --- a/src/auth/oauth/utils/attach.js +++ b/src/auth/oauth/utils/attach.js @@ -25,17 +25,16 @@ module.exports = async function attach(account, user) { await pipeline.exec().then(handlePipeline); - const userMetadata = new UserMetadata(redis); const updateParams = { - userId, - audience, metadata: { $set: { [provider]: profile, }, }, }; - await userMetadata.batchUpdate(updateParams); + await UserMetadata + .for(userId, audience, redis) + .batchUpdate(updateParams); return profile; }; diff --git a/src/auth/oauth/utils/detach.js b/src/auth/oauth/utils/detach.js index 3ddbbf3d4..408423da0 100644 --- a/src/auth/oauth/utils/detach.js +++ b/src/auth/oauth/utils/detach.js @@ -30,10 +30,7 @@ module.exports = async function detach(provider, userData) { await pipeline.exec().then(handlePipeline); - const userMetadata = new UserMetadata(redis); const updateParams = { - userId, - audience, metadata: { $remove: [ provider, @@ -41,5 +38,7 @@ module.exports = async function detach(provider, userData) { }, }; - return userMetadata.batchUpdate(updateParams); + return UserMetadata + .for(userId, audience, redis) + .batchUpdate(updateParams); }; diff --git a/src/custom/cappasity-users-activate.js b/src/custom/cappasity-users-activate.js index 0c1edb930..5a24186d6 100644 --- a/src/custom/cappasity-users-activate.js +++ b/src/custom/cappasity-users-activate.js @@ -13,7 +13,6 @@ module.exports = function mixPlan(userId, params) { const { payments } = config; const route = [payments.prefix, payments.routes.planGet].join('.'); const id = 'free'; - const userMetadata = new UserMetadata(this.redis); return amqp .publishAndWait(route, id, { timeout: 5000 }) @@ -22,8 +21,6 @@ module.exports = function mixPlan(userId, params) { const subscription = find(plan.subs, ['name', 'month']); const nextCycle = moment().add(1, 'month').valueOf(); const updateParams = { - userId, - audience, metadata: { $set: { plan: id, @@ -37,6 +34,8 @@ module.exports = function mixPlan(userId, params) { }, }; - return userMetadata.batchUpdate(updateParams); + return UserMetadata + .for(userId, audience, this.redis) + .batchUpdate(updateParams); }); }; diff --git a/src/custom/rfx-create-room-on-activate.js b/src/custom/rfx-create-room-on-activate.js index c48f4d345..5a84db0aa 100644 --- a/src/custom/rfx-create-room-on-activate.js +++ b/src/custom/rfx-create-room-on-activate.js @@ -22,8 +22,6 @@ function createRoom(userId, params, metadata) { name: `${metadata[audience].stationName} | ${metadata[audience].stationSchool}`, }; - const userMetadata = new UserMetadata(this.redis); - return amqp.publishAndWait(route, roomParams, { timeout: 5000 }) .bind(this) .then((room) => { @@ -37,7 +35,9 @@ function createRoom(userId, params, metadata) { }, }; - return userMetadata.batchUpdate(updateParams); + return UserMetadata + .for(userId, audience, this.redis) + .batchUpdate(updateParams); }); } diff --git a/src/utils/metadata/organization.js b/src/utils/metadata/organization.js index df8af93fd..6acd5d58a 100644 --- a/src/utils/metadata/organization.js +++ b/src/utils/metadata/organization.js @@ -11,10 +11,15 @@ class OrganizationMetadata { */ constructor(redis) { this.redis = redis; - this.meta = new MetaUpdate(this.redis, ORGANIZATIONS_METADATA); + this.metadata = new MetaUpdate(this.redis, ORGANIZATIONS_METADATA); this.audience = new Audience(this.redis, ORGANIZATIONS_AUDIENCE); } + async syncAudience(organizationId) { + const metaKeyTemplate = this.metadata.getMetadataKey('{{ID}}', '{{AUDIENCE}}'); + return this.audience.resyncSet(organizationId, metaKeyTemplate); + } + /** * Updates metadata on a organization object * @param {Object} opts @@ -23,7 +28,13 @@ class OrganizationMetadata { async batchUpdate(opts) { const { organizationId, ...restOpts } = opts; await this.audience.add(organizationId, restOpts.audience); - return this.meta.batchUpdate({ id: organizationId, ...restOpts }); + const updateResult = await this.metadata + .batchUpdate({ + id: organizationId, + ...opts, + }); + await this.syncAudience(organizationId); + return updateResult; } } diff --git a/src/utils/metadata/redis/audience.js b/src/utils/metadata/redis/audience.js index 395a236bc..d207d84b7 100644 --- a/src/utils/metadata/redis/audience.js +++ b/src/utils/metadata/redis/audience.js @@ -54,6 +54,30 @@ class Audience { assert(isNotEmptyString(audience) || Array.isArray(audience), 'must be not empty string or Array'); return redis.srem(this.getAudienceKey(id), audience); } + + /** + * Synchronizes audience list with currently available metadata + * @param id + * @param metadataKeyTemplate - format '{{ID}}!yourMetadataClass!{{AUDIENCE}}' + * @param redis + * @returns {*} + */ + resyncSet(id, metadataKeyTemplate, redis = this.redis) { + assert(isRedis(this.redis), 'must be ioredis instance'); + assert(isNotEmptyString(metadataKeyTemplate), 'must be not empty string'); + const luaScript = ` + local audiences = redis.call("SMEMBERS", KEYS[1]) + for _, audience in pairs(audiences) do + local metaKey = string.gsub(KEYS[2], '{{ID}}', '${id}') + metaKey = string.gsub(metaKey, '{{AUDIENCE}}', audience) + local keyLen = redis.call("HLEN", metaKey) + if (keyLen < 1) then + redis.call('SREM', KEYS[1], audience) + end + end + `; + return redis.eval(luaScript, 2, this.getAudienceKey(id), metadataKeyTemplate); + } } module.exports = Audience; diff --git a/src/utils/metadata/redis/update-metadata.js b/src/utils/metadata/redis/update-metadata.js index 41d82bfbd..2fd1cf3f5 100644 --- a/src/utils/metadata/redis/update-metadata.js +++ b/src/utils/metadata/redis/update-metadata.js @@ -90,7 +90,7 @@ class UpdateMetadata { async batchUpdate(opts) { const { redis } = this; const { - id, audience, metadata, script, + id, metadata, audience, script, } = opts; // we use own pipeline or Promise here diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js index 074bef656..33875282c 100644 --- a/src/utils/metadata/user.js +++ b/src/utils/metadata/user.js @@ -11,53 +11,71 @@ const { USERS_METADATA, USERS_AUDIENCE } = require('../../constants'); class UserMetadata { /** * @param {ioredis|Pipeline} redis + * @param {String|Number} userId + * @param {Staing|Array} audience */ - constructor(redis) { + constructor(redis, userId, audience) { this.pipeline = redis instanceof Pipeline; this.redis = redis; + this.userAudience = audience; + this.userId = userId; this.metadata = new MetaUpdate(this.redis, USERS_METADATA); this.audience = new Audience(this.redis, USERS_AUDIENCE); } /** * Updates metadata field on a user object - * @param {String|Number} id * @param {Object} values - * @param {String} audience + * @param {String} [audience] * @returns {Promise|void} */ - update(id, hashKey, value, audience) { + update(hashKey, value, audience = this.userAudience) { const work = [ - this.audience.add(id, audience), - this.metadata.update(id, audience, hashKey, value), + this.audience.add(this.userId, audience), + this.metadata.update(this.userId, audience, hashKey, value), ]; - return this.pipeline ? Promise.all(work) : work; + return this.pipeline ? work : Promise.all(work); } /** * Updates metadata on a user object using fields and values from provided Object - * @param {String|Number} id * @param {Object} values - * @param {String} audience + * @param {String} [audience] * @returns {Promise|void} */ - updateMulti(id, values, audience) { + updateMulti(values, audience = this.userAudience) { const work = [ - this.audience.add(id, audience), - this.metadata.updateMulti(id, audience, values), + this.audience.add(this.userId, audience), + this.metadata.updateMulti(this.userId, audience, values), ]; - return this.pipeline ? Promise.all(work) : work; + return this.pipeline ? work : Promise.all(work); } /** * Deletes key from user metadata object * @param {String|Number} id * @param {String} hashKey - * @param {String} audience + * @param {String} [audience] * @returns {Promise|void} */ - delete(id, hashKey, audience) { - return this.metadata.delete(id, audience, hashKey); + delete(hashKey, audience = this.userAudience) { + return this.pipeline ? this.deletePipeline(hashKey, audience) : this.deleteAsync(hashKey, audience); + } + + deletePipeline(hashKey, audience) { + this.metadata.delete(this.userId, audience, hashKey); + return this.syncAudience(); + } + + async deleteAsync(hashKey, audience) { + const result = await this.metadata.delete(this.userId, audience, hashKey); + await this.syncAudience(); + return result; + } + + async syncAudience() { + const metaKeyTemplate = this.metadata.getMetadataKey('{{ID}}', '{{AUDIENCE}}'); + return this.audience.resyncSet(this.userId, metaKeyTemplate); } /** @@ -66,9 +84,19 @@ class UserMetadata { * @return {Promise} */ async batchUpdate(opts) { - const { userId, ...restOpts } = opts; - await this.audience.add(userId, restOpts.audience); - return this.metadata.batchUpdate({ id: userId, ...restOpts }); + await this.audience.add(this.userId, this.userAudience); + const updateResult = await this.metadata + .batchUpdate({ + id: this.userId, + audience: this.userAudience, + ...opts, + }); + await this.syncAudience(); + return updateResult; + } + + static for(userId, audience, redis) { + return new UserMetadata(redis, userId, audience); } } diff --git a/src/utils/organization/add-organization-members.js b/src/utils/organization/add-organization-members.js index 5e397b079..7437013ab 100644 --- a/src/utils/organization/add-organization-members.js +++ b/src/utils/organization/add-organization-members.js @@ -43,7 +43,6 @@ async function addOrganizationMembers(opts) { const createdMembers = await registerOrganizationMembers.call(this, notRegisteredMembers); const pipe = redis.pipeline(); - const userMetadata = new UserMetadata(pipe); const membersKey = redisKey(organizationId, ORGANIZATIONS_MEMBERS); const organizationMembers = registeredMembers.concat(createdMembers); organizationMembers.forEach(({ password, ...member }) => { @@ -54,7 +53,9 @@ async function addOrganizationMembers(opts) { member.permissions = member.permissions || []; const stringifyMember = mapValues(member, JSONStringify); pipe.hmset(memberKey, stringifyMember); - userMetadata.update(member.id, organizationId, stringifyMember.permissions, audience); + UserMetadata + .for(member.id, audience, pipe) + .update(organizationId, stringifyMember.permissions); pipe.zadd(membersKey, stringifyMember.invited, memberKey); }); diff --git a/src/utils/organization/register-organization-members.js b/src/utils/organization/register-organization-members.js index 0006cc061..ebc39e2e8 100644 --- a/src/utils/organization/register-organization-members.js +++ b/src/utils/organization/register-organization-members.js @@ -36,18 +36,17 @@ async function registerOrganizationMember(member) { pipeline.hset(USERS_USERNAME_TO_ID, email, userId); await pipeline.exec().then(handlePipeline); - const userMetadata = new UserMetadata(redis); - await userMetadata.batchUpdate({ - userId, - audience, - metadata: [{ - $set: { - [USERS_ID_FIELD]: userId, - [USERS_USERNAME_FIELD]: email, - [USERS_CREATED_FIELD]: basicInfo[USERS_CREATED_FIELD], - }, - }], - }); + await UserMetadata + .for(userId, audience, redis) + .batchUpdate({ + metadata: [{ + $set: { + [USERS_ID_FIELD]: userId, + [USERS_USERNAME_FIELD]: email, + [USERS_CREATED_FIELD]: basicInfo[USERS_CREATED_FIELD], + }, + }], + }); // perform instant activation // internal username index const regPipeline = redis.pipeline().sadd(USERS_INDEX, userId); diff --git a/src/utils/update-metadata.js b/src/utils/update-metadata.js deleted file mode 100644 index f93ea9070..000000000 --- a/src/utils/update-metadata.js +++ /dev/null @@ -1,144 +0,0 @@ -/* eslint-disable no-mixed-operators */ -const Promise = require('bluebird'); -const mapValues = require('lodash/mapValues'); -const is = require('is'); -const { HttpStatusError } = require('common-errors'); -const redisKey = require('../utils/key'); -const sha256 = require('./sha256'); -const handlePipeline = require('../utils/pipeline-error'); -const { USERS_METADATA } = require('../constants'); - -const JSONStringify = (data) => JSON.stringify(data); - -/** - * Process metadata update operation for a passed audience - * @param {Object} pipeline - * @param {String} audience - * @param {Object} metadata - */ -function handleAudience(pipeline, key, metadata) { - const { $remove } = metadata; - const $removeOps = $remove && $remove.length || 0; - if ($removeOps > 0) { - pipeline.hdel(key, $remove); - } - - const { $set } = metadata; - const $setKeys = $set && Object.keys($set); - const $setLength = $setKeys && $setKeys.length || 0; - if ($setLength > 0) { - pipeline.hmset(key, mapValues($set, JSONStringify)); - } - - const { $incr } = metadata; - const $incrFields = $incr && Object.keys($incr); - const $incrLength = $incrFields && $incrFields.length || 0; - if ($incrLength > 0) { - $incrFields.forEach((fieldName) => { - pipeline.hincrby(key, fieldName, $incr[fieldName]); - }); - } - - return { - $removeOps, $setLength, $incrLength, $incrFields, - }; -} - -/** - * Maps updateMetadata ops - * @param {Array} responses - * @param {Array} operations - * @return {Object|Array} - */ -function mapMetaResponse(operations, responses) { - let cursor = 0; - return Promise - .map(operations, (props) => { - const { - $removeOps, $setLength, $incrLength, $incrFields, - } = props; - const output = {}; - - if ($removeOps > 0) { - output.$remove = responses[cursor]; - cursor += 1; - } - - if ($setLength > 0) { - output.$set = responses[cursor]; - cursor += 1; - } - - if ($incrLength > 0) { - const $incrResponse = output.$incr = {}; - $incrFields.forEach((fieldName) => { - $incrResponse[fieldName] = responses[cursor]; - cursor += 1; - }); - } - - return output; - }) - .then((ops) => (ops.length > 1 ? ops : ops[0])); -} - -/** - * Handle script, mutually exclusive with metadata - * @param {Array} scriptKeys - * @param {Array} responses - */ -function mapScriptResponse(scriptKeys, responses) { - const output = {}; - scriptKeys.forEach((fieldName, idx) => { - output[fieldName] = responses[idx]; - }); - return output; -} - -/** - * Updates metadata on a user object - * @param {Object} opts - * @return {Promise} - */ -function updateMetadata(opts) { - const { redis } = this; - const { - userId, audience, metadata, script, - } = opts; - const audiences = is.array(audience) ? audience : [audience]; - - // keys - const keys = audiences.map((aud) => redisKey(userId, USERS_METADATA, aud)); - - // if we have meta, then we can - if (metadata) { - const pipe = redis.pipeline(); - const metaOps = is.array(metadata) ? metadata : [metadata]; - - if (metaOps.length !== audiences.length) { - return Promise.reject(new HttpStatusError(400, 'audiences must match metadata entries')); - } - - const operations = metaOps.map((meta, idx) => handleAudience(pipe, keys[idx], meta)); - return pipe.exec() - .then(handlePipeline) - .then((res) => mapMetaResponse(operations, res)); - } - - // dynamic scripts - const $scriptKeys = Object.keys(script); - const scripts = $scriptKeys.map((scriptName) => { - const { lua, argv = [] } = script[scriptName]; - const sha = sha256(lua); - const name = `ms_users_${sha}`; - if (!is.fn(redis[name])) { - redis.defineCommand(name, { lua }); - } - return redis[name](keys.length, keys, argv); - }); - - return Promise.all(scripts).then((res) => mapScriptResponse($scriptKeys, res)); -} - -updateMetadata.handleAudience = handleAudience; -module.exports = updateMetadata; diff --git a/test/suites/update-metadata.js b/test/suites/update-metadata.js index d312e23d1..b1320b23c 100644 --- a/test/suites/update-metadata.js +++ b/test/suites/update-metadata.js @@ -103,59 +103,51 @@ describe('#updateMetadata', function getMetadataSuite() { ]); }); - it('tracks audienceList', async function test() { - const params = { - username, - audience: [ - audience, - '*.extra', - ], - metadata: [ - { - $set: { - x: 10, - b: 12, - c: 'cval', - }, - }, { - $set: { - x: 20, - b: 22, - c: 'xval', + describe('tracks audienceList', function audienceTrackSuite() { + beforeEach(async function updateUser() { + const params = { + username, + audience: [ + audience, + '*.extra', + ], + metadata: [ + { + $set: { + x: 10, + b: 12, + c: 'cval', + }, + }, { + $set: { + x: 20, + b: 22, + c: 'xval', + }, }, - }, - ], - }; - - await this.dispatch('users.updateMetadata', params); - const audiencesList = await this.users.redis.smembers(`${this.userId}!users-audiences`); - expect(audiencesList).to.include.members(['*.localhost', '*.extra']); - }); + ], + }; - it('must be able to run dynamic scripts / default namespace available', async function test() { - const lua = ` - local t = {} - table.insert(t, "foo") - local jsonDec = cjson.decode('{"bar": 1}') - local typeCheck = type(t) - redis.call("SET", "fookey", 777); - return {jsonDec.bar, redis.call("TIME"), redis.call("GET", "fookey"), typeCheck, unpack(t)} - `; + await this.dispatch('users.updateMetadata', params); + }); + it('adds audience', async function test() { + const audiencesList = await this.users.redis.smembers(`${this.userId}!users-audiences`); + expect(audiencesList).to.include.members(['*.localhost', '*.extra']); + }); - const params = { - username, - audience: [audience], - script: { - check: { - lua, - argv: ['nom-nom'], - }, - }, - }; - const updated = await this.dispatch('users.updateMetadata', params); - const [jsonVal, redisTime, keyValue] = updated.check; - expect(jsonVal).to.be.eq(1); - expect(redisTime).to.be.an('array'); - expect(keyValue).to.be.eq('777'); + it('deletes audience when no metadata left', async function test() { + const deleteParams = { + username, + audience: ['*.extra'], + metadata: [ + { + $rem: ['x', 'b', 'c'], + }, + ], + }; + await this.dispatch('users.updateMetadata', deleteParams); + const audiencesList = await this.users.redis.smembers(`${this.userId}!users-audiences`); + expect(audiencesList).to.include.members(['*.localhost']); + }); }); }); From bf27314d5740cfb78960386445544f7e59bee092 Mon Sep 17 00:00:00 2001 From: pajgo Date: Fri, 1 Nov 2019 15:13:06 +0600 Subject: [PATCH 07/13] feat: update user and organization metadata * UserMetadata for->using --- src/actions/alias.js | 2 +- src/actions/ban.js | 4 ++-- src/actions/organization/members/permission.js | 2 +- src/actions/organization/members/remove.js | 2 +- src/actions/register.js | 2 +- src/actions/updateMetadata.js | 2 +- src/auth/oauth/utils/attach.js | 2 +- src/auth/oauth/utils/detach.js | 2 +- src/custom/cappasity-users-activate.js | 2 +- src/custom/rfx-create-room-on-activate.js | 2 +- src/utils/metadata/user.js | 2 +- src/utils/organization/add-organization-members.js | 2 +- src/utils/organization/register-organization-members.js | 2 +- 13 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/actions/alias.js b/src/actions/alias.js index 0393e9624..616785c78 100644 --- a/src/actions/alias.js +++ b/src/actions/alias.js @@ -74,7 +74,7 @@ async function assignAlias({ params }) { pipeline.hset(key(userId, USERS_DATA), USERS_ALIAS_FIELD, alias); UserMetadata - .for(userId, defaultAudience, pipeline) + .using(userId, defaultAudience, pipeline) .update(USERS_ALIAS_FIELD, JSON.stringify(alias)); if (activeUser) { diff --git a/src/actions/ban.js b/src/actions/ban.js index d9ce7d207..9ff92c1b5 100644 --- a/src/actions/ban.js +++ b/src/actions/ban.js @@ -31,7 +31,7 @@ function lockUser({ pipeline.hset(redisKey(id, USERS_DATA), USERS_BANNED_FLAG, 'true'); // set .banned on metadata for filtering & sorting users by that field UserMetadata - .for(id, defaultAudience, pipeline) + .using(id, defaultAudience, pipeline) .updateMulti(mapValues(data, stringify)); pipeline.del(redisKey(id, USERS_TOKENS)); @@ -46,7 +46,7 @@ function unlockUser({ id }) { pipeline.hdel(redisKey(id, USERS_DATA), USERS_BANNED_FLAG); // remove .banned on metadata for filtering & sorting users by that field UserMetadata - .for(id, defaultAudience, pipeline) + .using(id, defaultAudience, pipeline) .delete([ 'banned', USERS_BANNED_DATA, diff --git a/src/actions/organization/members/permission.js b/src/actions/organization/members/permission.js index ef8cb0169..bdc4aa1ef 100644 --- a/src/actions/organization/members/permission.js +++ b/src/actions/organization/members/permission.js @@ -44,7 +44,7 @@ async function setOrganizationMemberPermission({ params }) { const pipeline = redis.pipeline(); UserMetadata - .for(userId, audience, pipeline) + .using(userId, audience, pipeline) .update(organizationId, permissions); pipeline.hset(redisKey(organizationId, ORGANIZATIONS_MEMBERS, userId), 'permissions', permissions); diff --git a/src/actions/organization/members/remove.js b/src/actions/organization/members/remove.js index 26d0381cb..30292f7dd 100644 --- a/src/actions/organization/members/remove.js +++ b/src/actions/organization/members/remove.js @@ -38,7 +38,7 @@ async function removeMember({ params }) { pipeline.del(memberKey); pipeline.zrem(redisKey(organizationId, ORGANIZATIONS_MEMBERS), memberKey); UserMetadata - .for(userId, audience, pipeline) + .using(userId, audience, pipeline) .delete(organizationId); return pipeline.exec().then(handlePipeline); diff --git a/src/actions/register.js b/src/actions/register.js index c31efd0e8..6e81a5bd2 100644 --- a/src/actions/register.js +++ b/src/actions/register.js @@ -213,7 +213,7 @@ async function performRegistration({ service, params }) { await pipeline.exec().then(handlePipeline); await UserMetadata - .for(userId, audience, service.redis) + .using(userId, audience, service.redis) .batchUpdate({ metadata: audience.map((metaAudience) => ({ $set: Object.assign(metadata[metaAudience] || {}, metaAudience === defaultAudience && { diff --git a/src/actions/updateMetadata.js b/src/actions/updateMetadata.js index a4a1b5e1b..d60558ed1 100644 --- a/src/actions/updateMetadata.js +++ b/src/actions/updateMetadata.js @@ -25,7 +25,7 @@ module.exports = async function updateMetadataAction(request) { .then(getUserId); return UserMetadata - .for(userId, audience, this.redis) + .using(userId, audience, this.redis) .batchUpdate(updateParams); }; diff --git a/src/auth/oauth/utils/attach.js b/src/auth/oauth/utils/attach.js index ebd90c7d4..cc71642a3 100644 --- a/src/auth/oauth/utils/attach.js +++ b/src/auth/oauth/utils/attach.js @@ -33,7 +33,7 @@ module.exports = async function attach(account, user) { }, }; await UserMetadata - .for(userId, audience, redis) + .using(userId, audience, redis) .batchUpdate(updateParams); return profile; diff --git a/src/auth/oauth/utils/detach.js b/src/auth/oauth/utils/detach.js index 408423da0..c008ae1f1 100644 --- a/src/auth/oauth/utils/detach.js +++ b/src/auth/oauth/utils/detach.js @@ -39,6 +39,6 @@ module.exports = async function detach(provider, userData) { }; return UserMetadata - .for(userId, audience, redis) + .using(userId, audience, redis) .batchUpdate(updateParams); }; diff --git a/src/custom/cappasity-users-activate.js b/src/custom/cappasity-users-activate.js index 5a24186d6..8b9d105ab 100644 --- a/src/custom/cappasity-users-activate.js +++ b/src/custom/cappasity-users-activate.js @@ -35,7 +35,7 @@ module.exports = function mixPlan(userId, params) { }; return UserMetadata - .for(userId, audience, this.redis) + .using(userId, audience, this.redis) .batchUpdate(updateParams); }); }; diff --git a/src/custom/rfx-create-room-on-activate.js b/src/custom/rfx-create-room-on-activate.js index 5a84db0aa..cbfb95a2b 100644 --- a/src/custom/rfx-create-room-on-activate.js +++ b/src/custom/rfx-create-room-on-activate.js @@ -36,7 +36,7 @@ function createRoom(userId, params, metadata) { }; return UserMetadata - .for(userId, audience, this.redis) + .using(userId, audience, this.redis) .batchUpdate(updateParams); }); } diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js index 33875282c..a0537db35 100644 --- a/src/utils/metadata/user.js +++ b/src/utils/metadata/user.js @@ -95,7 +95,7 @@ class UserMetadata { return updateResult; } - static for(userId, audience, redis) { + static using(userId, audience, redis) { return new UserMetadata(redis, userId, audience); } } diff --git a/src/utils/organization/add-organization-members.js b/src/utils/organization/add-organization-members.js index 7437013ab..ccde8ec0f 100644 --- a/src/utils/organization/add-organization-members.js +++ b/src/utils/organization/add-organization-members.js @@ -54,7 +54,7 @@ async function addOrganizationMembers(opts) { const stringifyMember = mapValues(member, JSONStringify); pipe.hmset(memberKey, stringifyMember); UserMetadata - .for(member.id, audience, pipe) + .using(member.id, audience, pipe) .update(organizationId, stringifyMember.permissions); pipe.zadd(membersKey, stringifyMember.invited, memberKey); }); diff --git a/src/utils/organization/register-organization-members.js b/src/utils/organization/register-organization-members.js index ebc39e2e8..5522f9ecc 100644 --- a/src/utils/organization/register-organization-members.js +++ b/src/utils/organization/register-organization-members.js @@ -37,7 +37,7 @@ async function registerOrganizationMember(member) { await pipeline.exec().then(handlePipeline); await UserMetadata - .for(userId, audience, redis) + .using(userId, audience, redis) .batchUpdate({ metadata: [{ $set: { From 62c759bd0252e57b4d8554c3e9f6007255d20c47 Mon Sep 17 00:00:00 2001 From: pajgo Date: Wed, 6 Nov 2019 17:50:29 +0600 Subject: [PATCH 08/13] feat: update user and organization metadata * delete all metadata on remove --- src/actions/remove.js | 10 +++---- src/auth/oauth/utils/attach.js | 2 +- src/auth/oauth/utils/detach.js | 2 +- src/utils/metadata/organization.js | 4 +-- src/utils/metadata/redis/audience.js | 10 +++++++ .../redis/{update-metadata.js => metadata.js} | 29 ++++++++++++++----- src/utils/metadata/user.js | 24 +++++++++++++-- .../register-organization-members.js | 2 +- 8 files changed, 63 insertions(+), 20 deletions(-) rename src/utils/metadata/redis/{update-metadata.js => metadata.js} (89%) diff --git a/src/actions/remove.js b/src/actions/remove.js index 0cae563b6..fb74b5966 100644 --- a/src/actions/remove.js +++ b/src/actions/remove.js @@ -16,7 +16,6 @@ const { USERS_USERNAME_TO_ID, USERS_USERNAME_FIELD, USERS_DATA, - USERS_METADATA, USERS_TOKENS, USERS_ID_FIELD, USERS_ALIAS_FIELD, @@ -70,10 +69,11 @@ async function removeUser({ params }) { } const transaction = redis.pipeline(); - const userMetadata = new UserMetadata(redis); const alias = internal[USERS_ALIAS_FIELD]; const userId = internal[USERS_ID_FIELD]; const resolvedUsername = internal[USERS_USERNAME_FIELD]; + const metaAudiences = await UserMetadata.using(userId, null, redis).getAudience(); + const userMetadata = UserMetadata.using(userId, null, transaction); if (alias) { transaction.hdel(USERS_ALIAS_TO_ID, alias.toLowerCase(), alias); @@ -96,9 +96,9 @@ async function removeUser({ params }) { // remove metadata & internal data transaction.del(key(userId, USERS_DATA)); - // TODO fix if user has multiple audiences some data will remain - transaction.del(key(userId, USERS_METADATA, audience)); - transaction.del(userMetadata.audience.getAudienceKey(userId)); + for (const metaAudience of metaAudiences) { + userMetadata.deleteMetadata(metaAudience); + } // remove auth tokens transaction.del(key(userId, USERS_TOKENS)); diff --git a/src/auth/oauth/utils/attach.js b/src/auth/oauth/utils/attach.js index cc71642a3..0344b607f 100644 --- a/src/auth/oauth/utils/attach.js +++ b/src/auth/oauth/utils/attach.js @@ -23,7 +23,7 @@ module.exports = async function attach(account, user) { // link uid to user id pipeline.hset(USERS_SSO_TO_ID, uid, userId); - await pipeline.exec().then(handlePipeline); + handlePipeline(await pipeline.exec()); const updateParams = { metadata: { diff --git a/src/auth/oauth/utils/detach.js b/src/auth/oauth/utils/detach.js index c008ae1f1..300a08468 100644 --- a/src/auth/oauth/utils/detach.js +++ b/src/auth/oauth/utils/detach.js @@ -28,7 +28,7 @@ module.exports = async function detach(provider, userData) { // delete account reference pipeline.hdel(USERS_SSO_TO_ID, uid); - await pipeline.exec().then(handlePipeline); + handlePipeline(await pipeline.exec()); const updateParams = { metadata: { diff --git a/src/utils/metadata/organization.js b/src/utils/metadata/organization.js index 6acd5d58a..dd566a7ee 100644 --- a/src/utils/metadata/organization.js +++ b/src/utils/metadata/organization.js @@ -1,5 +1,5 @@ const Audience = require('./redis/audience'); -const MetaUpdate = require('./redis/update-metadata'); +const Metadata = require('./redis/metadata'); const { ORGANIZATIONS_METADATA, ORGANIZATIONS_AUDIENCE } = require('../../constants'); /** @@ -11,7 +11,7 @@ class OrganizationMetadata { */ constructor(redis) { this.redis = redis; - this.metadata = new MetaUpdate(this.redis, ORGANIZATIONS_METADATA); + this.metadata = new Metadata(this.redis, ORGANIZATIONS_METADATA); this.audience = new Audience(this.redis, ORGANIZATIONS_AUDIENCE); } diff --git a/src/utils/metadata/redis/audience.js b/src/utils/metadata/redis/audience.js index d207d84b7..2e47b8d82 100644 --- a/src/utils/metadata/redis/audience.js +++ b/src/utils/metadata/redis/audience.js @@ -55,6 +55,16 @@ class Audience { return redis.srem(this.getAudienceKey(id), audience); } + /** + * Get list of assigned audiences + * @param {String|Number} id + * @param {ioredis|Pipeline}redis + * @returns {Promise|Pipeline} + */ + get(id, redis = this.redis) { + return redis.smembers(this.getAudienceKey(id)); + } + /** * Synchronizes audience list with currently available metadata * @param id diff --git a/src/utils/metadata/redis/update-metadata.js b/src/utils/metadata/redis/metadata.js similarity index 89% rename from src/utils/metadata/redis/update-metadata.js rename to src/utils/metadata/redis/metadata.js index 2fd1cf3f5..591bb4b1c 100644 --- a/src/utils/metadata/redis/update-metadata.js +++ b/src/utils/metadata/redis/metadata.js @@ -11,12 +11,12 @@ const isValidId = require('../../asserts/id'); const JSONStringify = (data) => JSON.stringify(data); /** - * Class handling metadata update operations using Redis backend + * Class handling metadata operations using Redis backend */ -class UpdateMetadata { +class Metadata { /** * @param {ioredis} redis or pipeline instance - * @param metadataKeyTemplate template base for Metadata key + * @param metadataKeyBase template base for Metadata key */ constructor(redis, metadataKeyBase) { assert(isRedis(redis), 'must be ioredis instance'); @@ -82,6 +82,19 @@ class UpdateMetadata { return redis.hdel(this.getMetadataKey(id, audience), key); } + /** + * Deletes metadata key + * @param id + * @param audience + * @param redis + * @returns {void|request.Request} + */ + deleteKey(id, audience, redis = this.redis) { + assert(isValidId(id), 'must be valid id'); + assert(isRedis(redis), 'must be ioredis instance'); + return redis.del(this.getMetadataKey(id, audience)); + } + /** * Updates metadata hash on provided Id * @param {Object} opts @@ -109,10 +122,10 @@ class UpdateMetadata { return Promise.reject(new HttpStatusError(400, 'audiences must match metadata entries')); } - const operations = metaOps.map((meta, idx) => UpdateMetadata.handleAudience(pipe, keys[idx], meta)); + const operations = metaOps.map((meta, idx) => Metadata.handleAudience(pipe, keys[idx], meta)); const result = handlePipeline(await pipe.exec()); - return UpdateMetadata.mapMetaResponse(operations, result); + return Metadata.mapMetaResponse(operations, result); } // dynamic scripts @@ -129,13 +142,13 @@ class UpdateMetadata { const result = await Promise.all(scripts); - return UpdateMetadata.mapScriptResponse($scriptKeys, result); + return Metadata.mapScriptResponse($scriptKeys, result); } /** * Process metadata update operation for a passed audience * @param {Object} pipeline - * @param {String} audience + * @param {String} key * @param {Object} metadata */ static handleAudience(pipeline, key, metadata) { @@ -218,4 +231,4 @@ class UpdateMetadata { } } -module.exports = UpdateMetadata; +module.exports = Metadata; diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js index a0537db35..37b524607 100644 --- a/src/utils/metadata/user.js +++ b/src/utils/metadata/user.js @@ -1,6 +1,6 @@ const Promise = require('bluebird'); const { Pipeline } = require('ioredis'); -const MetaUpdate = require('./redis/update-metadata'); +const Metadata = require('./redis/metadata'); const Audience = require('./redis/audience'); const { USERS_METADATA, USERS_AUDIENCE } = require('../../constants'); @@ -19,7 +19,7 @@ class UserMetadata { this.redis = redis; this.userAudience = audience; this.userId = userId; - this.metadata = new MetaUpdate(this.redis, USERS_METADATA); + this.metadata = new Metadata(this.redis, USERS_METADATA); this.audience = new Audience(this.redis, USERS_AUDIENCE); } @@ -73,6 +73,26 @@ class UserMetadata { return result; } + /** + * Deletes user metadata for passed audience + * @param {String} Audience + */ + deleteMetadata(audience) { + const work = [ + this.audience.delete(this.userId, audience), + this.metadata.deleteKey(this.userId, audience), + ]; + return this.pipeline ? work : Promise.all(work); + } + + /** + * Gets all audiences assigned + * @returns {*} + */ + getAudience() { + return this.audience.get(this.userId); + } + async syncAudience() { const metaKeyTemplate = this.metadata.getMetadataKey('{{ID}}', '{{AUDIENCE}}'); return this.audience.resyncSet(this.userId, metaKeyTemplate); diff --git a/src/utils/organization/register-organization-members.js b/src/utils/organization/register-organization-members.js index 5522f9ecc..a35ce76fd 100644 --- a/src/utils/organization/register-organization-members.js +++ b/src/utils/organization/register-organization-members.js @@ -34,7 +34,7 @@ async function registerOrganizationMember(member) { const userDataKey = redisKey(userId, USERS_DATA); pipeline.hmset(userDataKey, basicInfo); pipeline.hset(USERS_USERNAME_TO_ID, email, userId); - await pipeline.exec().then(handlePipeline); + handlePipeline(await pipeline.exec()); await UserMetadata .using(userId, audience, redis) From 10d173054bf8c92359c79cd5d14d669d0572ee68 Mon Sep 17 00:00:00 2001 From: pajgo Date: Fri, 8 Nov 2019 16:40:34 +0600 Subject: [PATCH 09/13] feat: update user and organization metadata * lua opt --- src/utils/metadata/redis/audience.js | 3 +-- src/utils/metadata/user.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils/metadata/redis/audience.js b/src/utils/metadata/redis/audience.js index 2e47b8d82..cf442a930 100644 --- a/src/utils/metadata/redis/audience.js +++ b/src/utils/metadata/redis/audience.js @@ -78,8 +78,7 @@ class Audience { const luaScript = ` local audiences = redis.call("SMEMBERS", KEYS[1]) for _, audience in pairs(audiences) do - local metaKey = string.gsub(KEYS[2], '{{ID}}', '${id}') - metaKey = string.gsub(metaKey, '{{AUDIENCE}}', audience) + local metaKey = string.gsub(KEYS[2], '{{AUDIENCE}}', audience) local keyLen = redis.call("HLEN", metaKey) if (keyLen < 1) then redis.call('SREM', KEYS[1], audience) diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js index 37b524607..b7da73115 100644 --- a/src/utils/metadata/user.js +++ b/src/utils/metadata/user.js @@ -94,7 +94,7 @@ class UserMetadata { } async syncAudience() { - const metaKeyTemplate = this.metadata.getMetadataKey('{{ID}}', '{{AUDIENCE}}'); + const metaKeyTemplate = this.metadata.getMetadataKey(this.userId, '{{AUDIENCE}}'); return this.audience.resyncSet(this.userId, metaKeyTemplate); } From f9c042d64344f947ba1ad186ee1920c8b9f1ed64 Mon Sep 17 00:00:00 2001 From: pajgo Date: Fri, 8 Nov 2019 20:17:58 +0600 Subject: [PATCH 10/13] feat: delete inactive users * rework on new MetaData --- rfcs/inactive_users/user_cleanup.md | 35 ++++++ src/actions/activate.js | 22 +++- src/actions/register.js | 7 +- src/actions/remove.js | 74 ++---------- src/constants.js | 1 + src/utils/inactive-user/inactive-user.js | 52 +++++++++ .../inactive-user/redis/inactive-user.js | 22 ++++ src/utils/user/redis/user.js | 54 +++++++++ src/utils/user/user.js | 106 ++++++++++++++++++ test/suites/delete-inactive-users.js | 52 +++++++++ 10 files changed, 355 insertions(+), 70 deletions(-) create mode 100644 rfcs/inactive_users/user_cleanup.md create mode 100644 src/utils/inactive-user/inactive-user.js create mode 100644 src/utils/inactive-user/redis/inactive-user.js create mode 100644 src/utils/user/redis/user.js create mode 100644 src/utils/user/user.js create mode 100644 test/suites/delete-inactive-users.js diff --git a/rfcs/inactive_users/user_cleanup.md b/rfcs/inactive_users/user_cleanup.md new file mode 100644 index 000000000..0b3300cfb --- /dev/null +++ b/rfcs/inactive_users/user_cleanup.md @@ -0,0 +1,35 @@ +# Inactive user cleanup + +## Overview and motivation +For users who didn't pass the activation process, the service using TTL keyspace cleanup and only `Internal Data` deleted. +A lot of linked keys remain in the database, and this leads to keyspace pollution. +For better data handling and clean database structure, I introduce some changes in service logic: + +## General defs + - `inactive-users` [Described here](#inactive-users-index) + - `user-audiences` [Described here](user_and_organization_meta_update.md) + +## `inactive-users` index +It's an Additional Redis Sorted Set which contains the list of IDs of the `inactive` users. +Each item score is equal to the `timestamp` set on user creation. +To avoid hardcoded SET name new `USERS_INACTIVATED` constant introduced. + +#### Registration process +When the user succeeds registration but activation not requested, the new entry added to `inactive-users`. + +#### Activation process +When the user succeeds activation the entry deleted from `inactive-users`. + +#### Index cleanup +On `registration` cleanup method executes before user creation. +On `activation` cleanup method executes before any checks performed by `activation` action. +* Uses `dlock` to be sure that only one process runs. +* Gets outdated user list and deletes them. + +## TODO Organization Members --- Need additional information +The `organization add member` process doesn't have validation whether the user passed activation and allows +inviting inactive users into an organization. + +* Should We Delete Organization Members? + + diff --git a/src/actions/activate.js b/src/actions/activate.js index 007226bd8..a6cf56de5 100644 --- a/src/actions/activate.js +++ b/src/actions/activate.js @@ -5,8 +5,12 @@ const jwt = require('../utils/jwt.js'); const { getInternalData } = require('../utils/userData'); const getMetadata = require('../utils/get-metadata'); const handlePipeline = require('../utils/pipeline-error.js'); +const InactiveUser = require('../utils/inactive-user/inactive-user'); +const User = require('../utils/user/user'); + const { USERS_INDEX, + USERS_INACTIVATED, USERS_DATA, USERS_REFERRAL_INDEX, USERS_PUBLIC_INDEX, @@ -22,7 +26,7 @@ const { const Forbidden = new Errors.HttpStatusError(403, 'invalid token'); const Inactive = new Errors.HttpStatusError(412, 'expired token, please request a new email'); const Active = new Errors.HttpStatusError(409, 'account is already active, please use sign in form'); - +const NotFound = new Errors.HttpStatusError(404, 'account not found'); /** * Helper to determine if something is true */ @@ -126,6 +130,9 @@ function activateAccount(data, metadata) { .persist(userKey) .sadd(USERS_INDEX, userId); + /* delete user id from the inactive users index */ + pipeline.zrem(USERS_INACTIVATED, userId); + if (alias) { pipeline.sadd(USERS_PUBLIC_INDEX, userId); } @@ -152,6 +159,13 @@ function hook(userId) { return this.service.hook('users:activate', userId, { audience: this.audience }); } +async function checkUserDeleting(internalData) { + const user = new User(this); + if (await user.isUserDeleting(internalData.id)) { + throw NotFound; + } +} + /** * @api {amqp} .activate Activate User * @apiVersion 1.0.0 @@ -173,7 +187,7 @@ function hook(userId) { * @apiParam (Payload) {String} [audience] - additional metadata will be pushed there from custom hooks * */ -function activateAction({ params }) { +async function activateAction({ params }) { // TODO: add security logs // var remoteip = request.params.remoteip; const { token, username } = params; @@ -191,11 +205,15 @@ function activateAction({ params }) { erase: config.token.erase, }; + const inactiveUsers = new InactiveUser(this); + await inactiveUsers.deleteInactive(config.deleteInactiveAccounts); + return Promise .bind(context) .then(verifyRequest) .bind(this) .then((resolvedUsername) => getInternalData.call(this, resolvedUsername)) + .tap(checkUserDeleting) .then((internalData) => Promise.join( internalData, getMetadata.call(this, internalData[USERS_ID_FIELD], audience).get(audience) diff --git a/src/actions/register.js b/src/actions/register.js index 6e81a5bd2..65edd7cdf 100644 --- a/src/actions/register.js +++ b/src/actions/register.js @@ -22,6 +22,8 @@ const checkLimits = require('../utils/check-ip-limits'); const challenge = require('../utils/challenges/challenge'); const handlePipeline = require('../utils/pipeline-error'); const hashPassword = require('../utils/register/password/hash'); +const InactiveUser = require('../utils/inactive-user/inactive-user'); + const { USERS_REF, USERS_INDEX, @@ -172,6 +174,9 @@ async function performRegistration({ service, params }) { await verifySSO(service, params); } + const inactiveUsers = new InactiveUser(service); + await inactiveUsers.deleteInactive(config.deleteInactiveAccounts); + const [creatorAudience] = audience; const defaultAudience = last(audience); const userId = service.flake.next(); @@ -208,7 +213,7 @@ async function performRegistration({ service, params }) { pipeline.hset(USERS_USERNAME_TO_ID, username, userId); if (activate === false && config.deleteInactiveAccounts >= 0) { - pipeline.expire(userDataKey, config.deleteInactiveAccounts); + inactiveUsers.add(userId, created, pipeline); } await pipeline.exec().then(handlePipeline); diff --git a/src/actions/remove.js b/src/actions/remove.js index fb74b5966..eaa76c236 100644 --- a/src/actions/remove.js +++ b/src/actions/remove.js @@ -2,31 +2,14 @@ const { ActionTransport } = require('@microfleet/core'); const Promise = require('bluebird'); const Errors = require('common-errors'); const intersection = require('lodash/intersection'); -const get = require('../utils/get-value'); -const key = require('../utils/key'); const { getInternalData } = require('../utils/userData'); const getMetadata = require('../utils/get-metadata'); -const handlePipeline = require('../utils/pipeline-error'); -const UserMetadata = require('../utils/metadata/user'); +const User = require('../utils/user/user'); +const InactiveUser = require('../utils/inactive-user/inactive-user'); const { - USERS_INDEX, - USERS_PUBLIC_INDEX, - USERS_ALIAS_TO_ID, - USERS_SSO_TO_ID, - USERS_USERNAME_TO_ID, - USERS_USERNAME_FIELD, - USERS_DATA, - USERS_TOKENS, USERS_ID_FIELD, - USERS_ALIAS_FIELD, USERS_ADMIN_ROLE, USERS_SUPER_ADMIN_ROLE, - USERS_ACTION_ACTIVATE, - USERS_ACTION_RESET, - USERS_ACTION_PASSWORD, - USERS_ACTION_REGISTER, - THROTTLE_PREFIX, - SSO_PROVIDERS, } = require('../constants'); // intersection of priority users @@ -68,55 +51,12 @@ async function removeUser({ params }) { throw new Errors.HttpStatusError(400, 'can\'t remove admin user from the system'); } - const transaction = redis.pipeline(); - const alias = internal[USERS_ALIAS_FIELD]; - const userId = internal[USERS_ID_FIELD]; - const resolvedUsername = internal[USERS_USERNAME_FIELD]; - const metaAudiences = await UserMetadata.using(userId, null, redis).getAudience(); - const userMetadata = UserMetadata.using(userId, null, transaction); + const user = new User(this); + const inactiveUser = new InactiveUser(this); - if (alias) { - transaction.hdel(USERS_ALIAS_TO_ID, alias.toLowerCase(), alias); - } - - transaction.hdel(USERS_USERNAME_TO_ID, resolvedUsername); - - // remove refs to SSO account - for (const provider of SSO_PROVIDERS) { - const uid = get(internal, `${provider}.uid`, { default: false }); - - if (uid) { - transaction.hdel(USERS_SSO_TO_ID, uid); - } - } - - // clean indices - transaction.srem(USERS_PUBLIC_INDEX, userId); - transaction.srem(USERS_INDEX, userId); - - // remove metadata & internal data - transaction.del(key(userId, USERS_DATA)); - for (const metaAudience of metaAudiences) { - userMetadata.deleteMetadata(metaAudience); - } - - // remove auth tokens - transaction.del(key(userId, USERS_TOKENS)); - - // remove throttling on actions - transaction.del(key(THROTTLE_PREFIX, USERS_ACTION_ACTIVATE, userId)); - transaction.del(key(THROTTLE_PREFIX, USERS_ACTION_PASSWORD, userId)); - transaction.del(key(THROTTLE_PREFIX, USERS_ACTION_REGISTER, userId)); - transaction.del(key(THROTTLE_PREFIX, USERS_ACTION_RESET, userId)); - - // complete it - const removeResult = await transaction - .exec() - .then(handlePipeline); - - // clear cache - const now = Date.now(); - await Promise.all([redis.fsortBust(USERS_INDEX, now), redis.fsortBust(USERS_PUBLIC_INDEX, now)]); + await inactiveUser.delete(internal.id); + const removeResult = await user.delete(internal); + await user.flushCaches(); return removeResult; } diff --git a/src/constants.js b/src/constants.js index 10f2033a2..9ff48fca1 100644 --- a/src/constants.js +++ b/src/constants.js @@ -6,6 +6,7 @@ module.exports = exports = { USERS_PUBLIC_INDEX: 'users-public', USERS_REFERRAL_INDEX: 'users-referral', ORGANIZATIONS_INDEX: 'organization-iterator-set', + // id mapping USERS_ALIAS_TO_ID: 'users-alias', USERS_SSO_TO_ID: 'users-sso-hash', diff --git a/src/utils/inactive-user/inactive-user.js b/src/utils/inactive-user/inactive-user.js new file mode 100644 index 000000000..801b114c3 --- /dev/null +++ b/src/utils/inactive-user/inactive-user.js @@ -0,0 +1,52 @@ +const Promise = require('bluebird'); +const RedisInactiveUser = require('./redis/inactive-user'); +const User = require('../user/user'); + +class InactiveUser { + constructor(service) { + this.service = service; + this.backend = new RedisInactiveUser(service.redis); + } + + acquireLock(lockName) { + const { dlock } = this.service; + return dlock + .once(lockName) + .disposer((l) => { + l.release().reflect(); + }); + } + + add(userId, created, pipeline = undefined) { + return this.backend.add(userId, created, pipeline); + } + + delete(userId) { + return this.backend.delete(userId); + } + + deleteInactive(userTTL) { + return Promise + .using(this.acquireLock('delete-inactive-users'), () => this._deleteInactive(userTTL)) + .catch({ name: 'LockAcquisitionError' }, () => {}); + } + + async _deleteInactive(userTTL) { + const user = new User(this.service); + const inactiveUsers = await this.backend.get(userTTL); + const work = []; + + for (const userId of inactiveUsers) { + work.push( + user + .delete(userId) + .then(() => this.backend.delete(userId)) + ); + } + await Promise.all(work); + + return inactiveUsers.length; + } +} + +module.exports = InactiveUser; diff --git a/src/utils/inactive-user/redis/inactive-user.js b/src/utils/inactive-user/redis/inactive-user.js new file mode 100644 index 000000000..43db96ec8 --- /dev/null +++ b/src/utils/inactive-user/redis/inactive-user.js @@ -0,0 +1,22 @@ +class InactiveUser { + static REDIS_KEY = 'users-inactivated'; + + constructor(redis) { + this.redis = redis; + } + + get(interval) { + const expire = Date.now() - (interval * 1000); + return this.redis.zrangebyscore(InactiveUser.REDIS_KEY, '-inf', expire); + } + + add(userId, createTime, redis = this.redis) { + return redis.zadd(InactiveUser.REDIS_KEY, createTime, userId); + } + + delete(userId, redis = this.redis) { + return redis.zrem(InactiveUser.REDIS_KEY, userId); + } +} + +module.exports = InactiveUser; diff --git a/src/utils/user/redis/user.js b/src/utils/user/redis/user.js new file mode 100644 index 000000000..f6b7e6ff0 --- /dev/null +++ b/src/utils/user/redis/user.js @@ -0,0 +1,54 @@ +const Promise = require('bluebird'); +const getInternalData = require('../../userData/get-internal-data'); + +const { + USERS_ALIAS_TO_ID, + USERS_SSO_TO_ID, + USERS_USERNAME_TO_ID, + USERS_INDEX, + USERS_PUBLIC_INDEX, + USERS_DATA, + USERS_TOKENS, +} = require('../../../constants'); + +class User { + constructor(redis) { + this.redis = redis; + } + + static getUserDataKey(userId) { + return `${userId}!${USERS_DATA}`; + } + + flushCaches() { + const now = Date.now(); + return Promise.all([ + this.redis.fsortBust(USERS_INDEX, now), + this.redis.fsortBust(USERS_PUBLIC_INDEX, now), + ]); + } + + getData(userId) { + return getInternalData.call({ redis: this.redis }, userId); + } + + delete({ id, username, alias, ssoIds }, redis = this.redis) { + const scriptKeys = [ + USERS_ALIAS_TO_ID, USERS_USERNAME_TO_ID, USERS_SSO_TO_ID, + USERS_PUBLIC_INDEX, USERS_INDEX, USERS_TOKENS, + User.getUserDataKey(id), + ]; + const luaScript = ` + ${alias === undefined ? '' : `redis.call("HDEL", KEYS[1], '${alias}', '${alias.toLowerCase()}')`} + redis.call("HDEL", KEYS[2], '${username}') + ${ssoIds.length > 0 ? `redis.call("HDEL", KEYS[3], ${ssoIds.map((uid) => `'${uid}'`).join(',')})` : ''} + redis.call("SREM", KEYS[4], '${id}') + redis.call("SREM", KEYS[5], '${id}') + redis.call("HDEL", KEYS[6], '${id}') + redis.call("DEL", KEYS[7]) + `; + return redis.eval(luaScript, scriptKeys.length, ...scriptKeys); + } +} + +module.exports = User; diff --git a/src/utils/user/user.js b/src/utils/user/user.js new file mode 100644 index 000000000..3f2f7a8bc --- /dev/null +++ b/src/utils/user/user.js @@ -0,0 +1,106 @@ +const Promise = require('bluebird'); +const { LockAcquisitionError } = require('ioredis-lock'); +const Errors = require('common-errors'); +const get = require('lodash/get'); +const handlePipeline = require('../pipeline-error'); +const RedisUser = require('./redis/user'); +const UserMetadata = require('../metadata/user'); + +const { + USERS_ACTION_ACTIVATE, USERS_ACTION_REGISTER, + USERS_ACTION_PASSWORD, USERS_ACTION_RESET, + SSO_PROVIDERS, +} = require('../../constants'); + + +class User { + constructor(service) { + this.service = service; + this.backend = new RedisUser(service.redis); + } + + acquireLock(lockName) { + const { dlock } = this.service; + return dlock + .once(lockName) + .catch(LockAcquisitionError, () => null); + } + + isUserDeleting(userId) { + return this.acquireLock(`delete-user-${userId}`) + .catch(LockAcquisitionError, () => true) + .return(false); + } + + getData(userId) { + return this.backend.getData(userId); + } + + flushCaches() { + return this.backend.flushCaches(); + } + + async delete(user, throwError = true) { + const { id, username, alias, ...restData } = typeof user === 'object' || await this.getData(user); + const lock = await this.acquireLock(`delete-user-${id}`); + + if (lock === null) { + if (throwError === true) { + throw new Errors.HttpStatusError(429, 'user marked for deletion'); + } + return null; + } + + try { + const { redis } = this.service; + const userAudiences = await UserMetadata.using(id, null, redis).getAudience(); + const pipeline = redis.pipeline(); + const pipelinedMetadata = UserMetadata.using(id, null, pipeline); + const ssoIds = []; + + for (const provider of SSO_PROVIDERS) { + const uid = get(restData, `${provider}.uid`); + if (uid) { + ssoIds.push(uid); + } + } + + for (const metaAudience of userAudiences) { + pipelinedMetadata.deleteMetadata(metaAudience, pipeline); + } + this.backend.delete({ id, username, alias, ssoIds }, pipeline); + const [pipelineResult] = await Promise.all([ + pipeline.exec(), + this.deleteUserTokens(username), + ]); + + handlePipeline(pipelineResult); + } finally { + lock.release().reflect(); + } + + return id; + } + + deleteUserTokens(userEmail) { + const actions = [ + USERS_ACTION_ACTIVATE, USERS_ACTION_REGISTER, + USERS_ACTION_PASSWORD, USERS_ACTION_RESET, + ]; + const { tokenManager } = this.service; + + const work = []; + + for (const action of actions) { + work.push( + tokenManager + .remove({ id: userEmail, action }) + .catch(() => {}) // ignore errors + ); + } + + return Promise.all(work); + } +} + +module.exports = User; diff --git a/test/suites/delete-inactive-users.js b/test/suites/delete-inactive-users.js new file mode 100644 index 000000000..57c9f6124 --- /dev/null +++ b/test/suites/delete-inactive-users.js @@ -0,0 +1,52 @@ +/* eslint-disable promise/always-return, no-prototype-builtins */ +const { inspectPromise } = require('@makeomatic/deploy'); +const Promise = require('bluebird'); + +const InactiveUsers = require('../../src/utils/inactive-user/inactive-user'); +const { createOrganization } = require('../helpers/organization'); + +describe('#inactive user', function registerSuite() { + beforeEach(global.startService.bind(this)); + afterEach(global.clearRedis.bind(this)); + + const regUser = { + username: 'v@makeomatic.ru', + audience: 'matic.ninja', + alias: 'bondthebest', + activate: false, + metadata: { + service: 'craft', + }, + // eslint-disable-next-line max-len + sso: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtcy11c2VycyIsInByb2ZpbGUiOnsiZDEiOjEyLCJubSI6InBhaiJ9LCJpbnRlcm5hbHMiOnsidWlkIjoxNTE2MjM5MDIyfSwicHJvdmlkZXIiOiJmYWNlYm9vayIsInVpZCI6MTUxNjIzOTAyMiwidXNlcm5hbWUiOiJmb29AYmFyLmJheiJ9.QXLcP-86A3ly-teJt_C_XQo3hFUVC0pVALb84Eitozo', + }; + + const regUserNoAlias = { + username: 'v2@makeomatic.ru', + audience: 'matic.log', + activate: false, + metadata: { + service: 'log', + }, + }; + + beforeEach(async () => { + this.users.config.deleteInactiveAccounts = 1; + + await createOrganization.call(this); + await this.dispatch('users.register', { ...regUser }); + await this.dispatch('users.register', { ...regUserNoAlias }); + }); + + it('deletes inactive user', async () => { + const inactiveUsers = new InactiveUsers(this.users); + await Promise.delay(1000); + + await inactiveUsers.deleteInactive(1); + + const { username } = regUser; + await this.dispatch('users.getInternalData', { username }) + .reflect() + .then(inspectPromise(false)); + }); +}); From d0304087ed211bffa5c1ba87c4d30f3270edb26c Mon Sep 17 00:00:00 2001 From: pajgo Date: Tue, 12 Nov 2019 18:26:39 +0600 Subject: [PATCH 11/13] feat: delete inactive users * org member remove --- src/actions/activate.js | 4 +- src/actions/register.js | 4 +- src/actions/remove.js | 2 +- src/utils/inactive-user/inactive-user.js | 52 --------- .../inactive-user/redis/inactive-user.js | 22 ---- src/utils/metadata/redis/metadata.js | 19 +++ src/utils/metadata/user.js | 16 ++- src/utils/organization/member/member.js | 35 ++++++ src/utils/organization/member/redis/member.js | 66 +++++++++++ src/utils/organization/organization.js | 41 +++++++ src/utils/organization/redis/organization.js | 43 +++++++ src/utils/user/inactive-user.js | 109 ++++++++++++++++++ src/utils/user/redis/inactive-user.js | 55 +++++++++ src/utils/user/redis/user.js | 41 +++++++ src/utils/user/user.js | 44 ++++++- test/suites/delete-inactive-users.js | 38 +++++- 16 files changed, 504 insertions(+), 87 deletions(-) delete mode 100644 src/utils/inactive-user/inactive-user.js delete mode 100644 src/utils/inactive-user/redis/inactive-user.js create mode 100644 src/utils/organization/member/member.js create mode 100644 src/utils/organization/member/redis/member.js create mode 100644 src/utils/organization/organization.js create mode 100644 src/utils/organization/redis/organization.js create mode 100644 src/utils/user/inactive-user.js create mode 100644 src/utils/user/redis/inactive-user.js diff --git a/src/actions/activate.js b/src/actions/activate.js index a6cf56de5..7fde57aa4 100644 --- a/src/actions/activate.js +++ b/src/actions/activate.js @@ -5,7 +5,7 @@ const jwt = require('../utils/jwt.js'); const { getInternalData } = require('../utils/userData'); const getMetadata = require('../utils/get-metadata'); const handlePipeline = require('../utils/pipeline-error.js'); -const InactiveUser = require('../utils/inactive-user/inactive-user'); +const InactiveUser = require('../utils/user/inactive-user'); const User = require('../utils/user/user'); const { @@ -206,7 +206,7 @@ async function activateAction({ params }) { }; const inactiveUsers = new InactiveUser(this); - await inactiveUsers.deleteInactive(config.deleteInactiveAccounts); + await inactiveUsers.cleanUsersOnce(config.deleteInactiveAccounts); return Promise .bind(context) diff --git a/src/actions/register.js b/src/actions/register.js index 65edd7cdf..3d62f80e3 100644 --- a/src/actions/register.js +++ b/src/actions/register.js @@ -22,7 +22,7 @@ const checkLimits = require('../utils/check-ip-limits'); const challenge = require('../utils/challenges/challenge'); const handlePipeline = require('../utils/pipeline-error'); const hashPassword = require('../utils/register/password/hash'); -const InactiveUser = require('../utils/inactive-user/inactive-user'); +const InactiveUser = require('../utils/user/inactive-user'); const { USERS_REF, @@ -175,7 +175,7 @@ async function performRegistration({ service, params }) { } const inactiveUsers = new InactiveUser(service); - await inactiveUsers.deleteInactive(config.deleteInactiveAccounts); + await inactiveUsers.cleanUsersOnce(config.deleteInactiveAccounts); const [creatorAudience] = audience; const defaultAudience = last(audience); diff --git a/src/actions/remove.js b/src/actions/remove.js index eaa76c236..49a4bc7b1 100644 --- a/src/actions/remove.js +++ b/src/actions/remove.js @@ -5,7 +5,7 @@ const intersection = require('lodash/intersection'); const { getInternalData } = require('../utils/userData'); const getMetadata = require('../utils/get-metadata'); const User = require('../utils/user/user'); -const InactiveUser = require('../utils/inactive-user/inactive-user'); +const InactiveUser = require('../utils/user/inactive-user'); const { USERS_ID_FIELD, USERS_ADMIN_ROLE, diff --git a/src/utils/inactive-user/inactive-user.js b/src/utils/inactive-user/inactive-user.js deleted file mode 100644 index 801b114c3..000000000 --- a/src/utils/inactive-user/inactive-user.js +++ /dev/null @@ -1,52 +0,0 @@ -const Promise = require('bluebird'); -const RedisInactiveUser = require('./redis/inactive-user'); -const User = require('../user/user'); - -class InactiveUser { - constructor(service) { - this.service = service; - this.backend = new RedisInactiveUser(service.redis); - } - - acquireLock(lockName) { - const { dlock } = this.service; - return dlock - .once(lockName) - .disposer((l) => { - l.release().reflect(); - }); - } - - add(userId, created, pipeline = undefined) { - return this.backend.add(userId, created, pipeline); - } - - delete(userId) { - return this.backend.delete(userId); - } - - deleteInactive(userTTL) { - return Promise - .using(this.acquireLock('delete-inactive-users'), () => this._deleteInactive(userTTL)) - .catch({ name: 'LockAcquisitionError' }, () => {}); - } - - async _deleteInactive(userTTL) { - const user = new User(this.service); - const inactiveUsers = await this.backend.get(userTTL); - const work = []; - - for (const userId of inactiveUsers) { - work.push( - user - .delete(userId) - .then(() => this.backend.delete(userId)) - ); - } - await Promise.all(work); - - return inactiveUsers.length; - } -} - -module.exports = InactiveUser; diff --git a/src/utils/inactive-user/redis/inactive-user.js b/src/utils/inactive-user/redis/inactive-user.js deleted file mode 100644 index 43db96ec8..000000000 --- a/src/utils/inactive-user/redis/inactive-user.js +++ /dev/null @@ -1,22 +0,0 @@ -class InactiveUser { - static REDIS_KEY = 'users-inactivated'; - - constructor(redis) { - this.redis = redis; - } - - get(interval) { - const expire = Date.now() - (interval * 1000); - return this.redis.zrangebyscore(InactiveUser.REDIS_KEY, '-inf', expire); - } - - add(userId, createTime, redis = this.redis) { - return redis.zadd(InactiveUser.REDIS_KEY, createTime, userId); - } - - delete(userId, redis = this.redis) { - return redis.zrem(InactiveUser.REDIS_KEY, userId); - } -} - -module.exports = InactiveUser; diff --git a/src/utils/metadata/redis/metadata.js b/src/utils/metadata/redis/metadata.js index 591bb4b1c..a0ab915dd 100644 --- a/src/utils/metadata/redis/metadata.js +++ b/src/utils/metadata/redis/metadata.js @@ -1,5 +1,6 @@ const Promise = require('bluebird'); const mapValues = require('lodash/mapValues'); +const pick = require('lodash/pick'); const assert = require('assert'); const { HttpStatusError } = require('common-errors'); const handlePipeline = require('../../pipeline-error'); @@ -38,6 +39,16 @@ class Metadata { return `${id}!${this.metadataKeyBase}!${audience}`; } + /** + * Gets metadata + * @param id + * @param audience + * @returns {*} + */ + get(id, audience) { + return this.redis.hgetall(this.getMetadataKey(id, audience)); + } + /** * Updates metadata hash key * @param {String} id @@ -229,6 +240,14 @@ class Metadata { }); return output; } + + static parse(data, fields) { + const parsedData = mapValues(data, JSON.parse); + if (fields) { + return pick(parsedData, fields); + } + return parsedData; + } } module.exports = Metadata; diff --git a/src/utils/metadata/user.js b/src/utils/metadata/user.js index b7da73115..94983c1cc 100644 --- a/src/utils/metadata/user.js +++ b/src/utils/metadata/user.js @@ -53,7 +53,6 @@ class UserMetadata { /** * Deletes key from user metadata object - * @param {String|Number} id * @param {String} hashKey * @param {String} [audience] * @returns {Promise|void} @@ -93,6 +92,21 @@ class UserMetadata { return this.audience.get(this.userId); } + /** + * Gets metadata for assigned audiences + * @param audiences + * @param fields + * @returns {Promise} + */ + async getMetadata(audiences = this.userAudience, fields = {}) { + const _audiences = Array.isArray(audiences) ? audiences : [audiences]; + const data = await Promise.map(_audiences, (a) => this.metadata.get(this.userId, a)); + const output = _audiences.map((audience, idx) => { + return Metadata.parse(data[idx], fields[audience]); + }); + return _audiences.length === 1 ? output[0] : output; + } + async syncAudience() { const metaKeyTemplate = this.metadata.getMetadataKey(this.userId, '{{AUDIENCE}}'); return this.audience.resyncSet(this.userId, metaKeyTemplate); diff --git a/src/utils/organization/member/member.js b/src/utils/organization/member/member.js new file mode 100644 index 000000000..15e1bb522 --- /dev/null +++ b/src/utils/organization/member/member.js @@ -0,0 +1,35 @@ +const RedisOrgMember = require('./redis/member'); + +/** + * Class handing Organization Member data + */ +class OrganizationMember { + /** + * @param {ioredis}redis + * @param {String|Number}memberId + * @param {String|Number}orgId + */ + constructor(redis, memberId, orgId) { + this.backend = new RedisOrgMember(redis); + this.id = memberId; + this.orgId = orgId; + } + + /** + * Deletes Organization Member record + * @returns {*} + */ + delete() { + return this.backend.delete(this.orgId, this.id); + } + + getOrganizationMemberKey(memberId = this.id, orgId = this.orgId) { + return RedisOrgMember.getRedisKey(orgId, memberId); + } + + static using(redis, memberId, orgId) { + return new OrganizationMember(redis, memberId, orgId); + } +} + +module.exports = OrganizationMember; diff --git a/src/utils/organization/member/redis/member.js b/src/utils/organization/member/redis/member.js new file mode 100644 index 000000000..07fca1493 --- /dev/null +++ b/src/utils/organization/member/redis/member.js @@ -0,0 +1,66 @@ +const mapValues = require('lodash/mapValues'); +const assert = require('assert'); +const { isRedis } = require('../../../asserts/redis'); +const isValidId = require('../../../asserts/id'); + +const { + ORGANIZATIONS_MEMBERS, +} = require('../../../../constants'); + +const JSONStringify = (data) => JSON.stringify(data); + +/** + * Class handling Organization member Redis backend + */ +class OrganizationMember { + /** + * @param {ioredis|Pipeline}redis + */ + constructor(redis) { + assert(isRedis(redis), 'must be a valid redis instance'); + this.redis = redis; + } + + /** + * Generates Organization member Redis key + * @param {String|Number}orgId + * @param {String|Number}memberId + * @returns {string} + */ + static getRedisKey(orgId, memberId) { + assert(isValidId(orgId), 'must be valid organization id'); + assert(isValidId(memberId), 'must be valid member id'); + return `${orgId}!${ORGANIZATIONS_MEMBERS}!${memberId}`; + } + + /** + * Deletes Organization Member key + * @param {String|Number}orgId + * @param {String|Number}memberId + * @param {ioredis|Pipeline}[redis] + * @returns {*} + */ + delete(orgId, memberId, redis = this.redis) { + assert(isRedis(redis), 'must be a valid redis instance'); + return redis.del(OrganizationMember.getRedisKey(orgId, memberId)); + } + + /** + * Updates Organization member hash contents + * @param {String|Number}orgId + * @param {String|Number}memberId + * @param {Object}params + * @param redis + * @returns {*} + */ + update(orgId, memberId, params, redis = this.redis) { + assert(isRedis(redis), 'must be a valid redis instance'); + return redis.hmset(OrganizationMember.getRedisKey(orgId, memberId), OrganizationMember.stringify(params)); + } + + static stringify(params) { + return mapValues(params, JSONStringify); + } +} + +module.exports = OrganizationMember; diff --git a/src/utils/organization/organization.js b/src/utils/organization/organization.js new file mode 100644 index 000000000..6150e3ed2 --- /dev/null +++ b/src/utils/organization/organization.js @@ -0,0 +1,41 @@ +const Promise = require('bluebird'); +const RedisOrganization = require('./redis/organization'); +const OrganizationMember = require('./member/member'); + +/** + * Class Handing Organization actions + */ +class Organization { + id = undefined; + + constructor(redis, orgId) { + this.redis = redis; + this.id = orgId; + this.backend = new RedisOrganization(redis); + } + + removeMember(memberId) { + const orgMember = OrganizationMember.using(this.redis, memberId, this.id); + const memberKey = orgMember.getOrganizationMemberKey(); + return Promise.all([ + orgMember.delete(), + this.backend.removeMember(this.id, memberKey, this.redis), + ]); + } + + static filterIds(obj) { + const ids = []; + const re = /^\d+$/; + for (const [key] of Object.entries(obj)) { + if (re.test(key)) ids.push(key); + } + return ids; + } + + static using(orgId, redis) { + const org = new Organization(redis, orgId); + return org; + } +} + +module.exports = Organization; diff --git a/src/utils/organization/redis/organization.js b/src/utils/organization/redis/organization.js new file mode 100644 index 000000000..f31dca62a --- /dev/null +++ b/src/utils/organization/redis/organization.js @@ -0,0 +1,43 @@ +const assert = require('assert'); +const { isRedis } = require('../../asserts/redis'); +const isValidId = require('../../asserts/id'); +const isNotEmptyString = require('../../asserts/string-not-empty'); + +const { ORGANIZATIONS_MEMBERS } = require('../../../constants'); + +/** + * Class handles Organization Data Redis Backend + */ +class Organization { + /** + * @param {ioredis|Pipeline}redis + */ + constructor(redis) { + assert(isRedis(redis), 'must be a valid redis instance'); + this.redis = redis; + } + + /** + * Gets Key with Organization information + * @param {String|Number}orgId + * @returns {string} + */ + static getMembersKey(orgId) { + assert(isValidId(orgId), 'must be valid organization id'); + return `${orgId}!${ORGANIZATIONS_MEMBERS}`; + } + + /** + * Deletes provided member key from Organization Members list + * @param {String|Number}orgId + * @param {String}memberKey + * @param {ioredis|Pipeline}[redis] + * @returns {*} + */ + removeMember(orgId, memberKey, redis = this.redis) { + assert(isNotEmptyString(memberKey), 'must be not epty string'); + return redis.zrem(Organization.getMembersKey(orgId), memberKey); + } +} + +module.exports = Organization; diff --git a/src/utils/user/inactive-user.js b/src/utils/user/inactive-user.js new file mode 100644 index 000000000..8d5042e35 --- /dev/null +++ b/src/utils/user/inactive-user.js @@ -0,0 +1,109 @@ +const Promise = require('bluebird'); +const assert = require('assert'); +const User = require('./user'); +const UserMetadata = require('../metadata/user'); +const Organization = require('../organization/organization'); +const RedisInactiveUser = require('./redis/inactive-user'); +const isNotEmptyString = require('../asserts/string-not-empty'); + +/** + * Class handling Inactive User actions + */ +class InactiveUser { + /** + * @param {Microfleet}service + * @param {ioredis}service.redis + * @param {dlock}service.dlock + * @param {Object}service.config + */ + constructor(service) { + this.service = service; + this.backend = new RedisInactiveUser(this.service.redis); + } + + /** + * Attempts to acquire operation lock using `dlock` + * @param {String}lockName + * @returns {FunctionDisposer} + */ + acquireLock(lockName) { + assert(isNotEmptyString(lockName), 'must be valid lock name'); + const { dlock } = this.service; + return dlock + .once(lockName) + .disposer((l) => { + l.release().reflect(); + }); + } + + /** + * Adds provided user id into inactive index + * @param {String|Number}userId + * @param {Number}created + * @param {ioredis|pipeline}[pipeline] + * @returns {Promise<*>|Pipeline} + */ + add(userId, created, pipeline = undefined) { + return this.backend.add(userId, created, pipeline); + } + + /** + * Deletes provided user id from inactive index + * @param {String|Number}userId + * @returns {Promise<*>|Pipeline} + */ + delete(userId) { + return this.backend.delete(userId); + } + + /** + * Acquires lock and executes `cleanUsers` + * @param {Number}userTTL - interval seconds + * @returns {Promise | * | Promise} + */ + cleanUsersOnce(userTTL) { + return Promise + .using(this.acquireLock('delete-inactive-users'), () => this.cleanUsers(userTTL)) + .catch({ name: 'LockAcquisitionError' }, () => {}); + } + + /** + * @CAUTION Deletes Users whose ids were in index and their score < Now()-interval + * @param {Number}userTTL -interval seconds + * @returns {Promise<*>} + */ + async cleanUsers(userTTL) { + const { config, redis } = this.service; + const { audience } = config.organizations; + const user = new User(this.service); + const work = []; + + const usersToDelete = await this.backend.get(userTTL); + const userOrganizations = {}; + + await Promise.map(usersToDelete, async (id) => { + const metaData = await UserMetadata + .using(id, audience, redis) + .getMetadata(); + userOrganizations[id] = Organization.filterIds(metaData); + }); + + for (const userId of usersToDelete) { + for (const orgId of userOrganizations[userId] || []) { + work.push( + Organization.using(orgId, redis).removeMember(userId) + ); + } + work.push( + user + .delete(userId) + .then(() => this.delete(userId)) + ); + } + await Promise.all(work); + + return usersToDelete.length; + } +} + +module.exports = InactiveUser; diff --git a/src/utils/user/redis/inactive-user.js b/src/utils/user/redis/inactive-user.js new file mode 100644 index 000000000..2b4397d06 --- /dev/null +++ b/src/utils/user/redis/inactive-user.js @@ -0,0 +1,55 @@ +const assert = require('assert'); +const { isRedis } = require('../../asserts/redis'); +const isValidId = require('../../asserts/id'); +const isInteger = require('../../asserts/integer'); + +/** + * Class Handling Inactive Users index using Redis backend + */ +class InactiveUser { + static REDIS_KEY = 'users-inactivated'; + + /** + * @param {ioredis|Pipeline}redis + */ + constructor(redis) { + assert(isRedis(redis), 'must be a valid redis instance'); + this.redis = redis; + } + + /** + * Get list of Ids having score < Now()-interval + * @param {Number}interval - seconds + * @returns {Promise|Pipeline} + */ + get(interval) { + assert(isInteger(interval), 'must be valid interval'); + const expire = Date.now() - (interval * 1000); + return this.redis.zrangebyscore(InactiveUser.REDIS_KEY, '-inf', expire); + } + + /** + * @param {String|Number}userId + * @param {Number}createTime - timestamp + * @param {ioredis|Pipeline}[redis] + * @returns {Promise<*>|Pipeline} + */ + add(userId, createTime, redis = this.redis) { + assert(isValidId(userId), 'must be valid user id'); + assert(isInteger(createTime), 'must be valid timestamp'); + return redis.zadd(InactiveUser.REDIS_KEY, createTime, userId); + } + + /** + * Deletes passed ID from index + * @param {String|Number}userId + * @param {ioredis|Pipeline}[redis] + * @returns {Promise<*>|Pipeline} + */ + delete(userId, redis = this.redis) { + assert(isValidId(userId), 'must be valid user id'); + return redis.zrem(InactiveUser.REDIS_KEY, userId); + } +} + +module.exports = InactiveUser; diff --git a/src/utils/user/redis/user.js b/src/utils/user/redis/user.js index f6b7e6ff0..122399969 100644 --- a/src/utils/user/redis/user.js +++ b/src/utils/user/redis/user.js @@ -1,4 +1,9 @@ const Promise = require('bluebird'); +const assert = require('assert'); +const { isRedis } = require('../../asserts/redis'); +const isValidId = require('../../asserts/id'); +const isNotEmptyString = require('../../asserts/string-not-empty'); + const getInternalData = require('../../userData/get-internal-data'); const { @@ -11,15 +16,32 @@ const { USERS_TOKENS, } = require('../../../constants'); +/** + * Class handles User data operations using Redis backend + */ class User { + /** + * @param {ioredis|Pipeline}redis + */ constructor(redis) { + assert(isRedis(redis), 'must be a valid redis instance'); this.redis = redis; } + /** + * Generates Redis Hash Key storing user data + * @param {String|Number}userId + * @returns {string} + */ static getUserDataKey(userId) { + assert(isValidId(userId), 'must be valid user id'); return `${userId}!${USERS_DATA}`; } + /** + * Flush caches generated by `redis-fsort` + * @returns {Promise<*>} + */ flushCaches() { const now = Date.now(); return Promise.all([ @@ -28,11 +50,30 @@ class User { ]); } + /** + * Returns User data + * @param {String|Number}userId + */ getData(userId) { + assert(isValidId(userId), 'must be valid user id'); return getInternalData.call({ redis: this.redis }, userId); } + /** + * Deletes User and associated data + * @param user + * @param {String|Number}user.id + * @param {String}user.username + * @param {String}user.alias + * @param {String[]}user.ssoIds + * @param {ioredis|Pipeline} [redis] + * @returns {any} + */ delete({ id, username, alias, ssoIds }, redis = this.redis) { + assert(isRedis(redis), 'must be a valid redis instance'); + assert(isValidId(id), 'must be valid user id'); + assert(isNotEmptyString(username), 'must be valid alias'); + const scriptKeys = [ USERS_ALIAS_TO_ID, USERS_USERNAME_TO_ID, USERS_SSO_TO_ID, USERS_PUBLIC_INDEX, USERS_INDEX, USERS_TOKENS, diff --git a/src/utils/user/user.js b/src/utils/user/user.js index 3f2f7a8bc..216e53d81 100644 --- a/src/utils/user/user.js +++ b/src/utils/user/user.js @@ -9,16 +9,29 @@ const UserMetadata = require('../metadata/user'); const { USERS_ACTION_ACTIVATE, USERS_ACTION_REGISTER, USERS_ACTION_PASSWORD, USERS_ACTION_RESET, + USERS_ACTION_ORGANIZATION_INVITE, SSO_PROVIDERS, } = require('../../constants'); - +/** + * Class managing User data and operations + */ class User { + /** + * @param {Microfleet}service + * @param {ioredis}service.redis + * @param {dlock}service.dlock + */ constructor(service) { this.service = service; this.backend = new RedisUser(service.redis); } + /** + * Attempts to acquire operation lock using `dlock` + * @param lockName + * @returns {Promise | *} + */ acquireLock(lockName) { const { dlock } = this.service; return dlock @@ -26,20 +39,39 @@ class User { .catch(LockAcquisitionError, () => null); } + /** + * Checks whether user is being deleted + * @param {String|Number}userId + * @returns {Promise} + */ isUserDeleting(userId) { return this.acquireLock(`delete-user-${userId}`) .catch(LockAcquisitionError, () => true) .return(false); } + /** + * Gets User data + * @param {String|Number}userId + */ getData(userId) { return this.backend.getData(userId); } + /** + * Flushes caches + * @returns {Promise<*>} + */ flushCaches() { return this.backend.flushCaches(); } + /** + * @CAUTION Deletes user + * @param {Object}user - User Data object + * @param {Boolean}[throwError] - Throws Race Condition error if Delete process was already started + * @returns {Promise} + */ async delete(user, throwError = true) { const { id, username, alias, ...restData } = typeof user === 'object' || await this.getData(user); const lock = await this.acquireLock(`delete-user-${id}`); @@ -53,8 +85,8 @@ class User { try { const { redis } = this.service; - const userAudiences = await UserMetadata.using(id, null, redis).getAudience(); const pipeline = redis.pipeline(); + const userAudiences = await UserMetadata.using(id, null, redis).getAudience(); const pipelinedMetadata = UserMetadata.using(id, null, pipeline); const ssoIds = []; @@ -68,7 +100,9 @@ class User { for (const metaAudience of userAudiences) { pipelinedMetadata.deleteMetadata(metaAudience, pipeline); } + this.backend.delete({ id, username, alias, ssoIds }, pipeline); + const [pipelineResult] = await Promise.all([ pipeline.exec(), this.deleteUserTokens(username), @@ -82,10 +116,16 @@ class User { return id; } + /** + * Cleans User Action tokens + * @param userEmail + * @returns {Promise<*>} + */ deleteUserTokens(userEmail) { const actions = [ USERS_ACTION_ACTIVATE, USERS_ACTION_REGISTER, USERS_ACTION_PASSWORD, USERS_ACTION_RESET, + USERS_ACTION_ORGANIZATION_INVITE, ]; const { tokenManager } = this.service; diff --git a/test/suites/delete-inactive-users.js b/test/suites/delete-inactive-users.js index 57c9f6124..ba5e7f69f 100644 --- a/test/suites/delete-inactive-users.js +++ b/test/suites/delete-inactive-users.js @@ -1,8 +1,10 @@ /* eslint-disable promise/always-return, no-prototype-builtins */ const { inspectPromise } = require('@makeomatic/deploy'); const Promise = require('bluebird'); - -const InactiveUsers = require('../../src/utils/inactive-user/inactive-user'); +const faker = require('faker'); +const ld = require('lodash'); +const { expect } = require('chai'); +const InactiveUsers = require('../../src/utils/user/inactive-user'); const { createOrganization } = require('../helpers/organization'); describe('#inactive user', function registerSuite() { @@ -31,8 +33,6 @@ describe('#inactive user', function registerSuite() { }; beforeEach(async () => { - this.users.config.deleteInactiveAccounts = 1; - await createOrganization.call(this); await this.dispatch('users.register', { ...regUser }); await this.dispatch('users.register', { ...regUserNoAlias }); @@ -42,11 +42,39 @@ describe('#inactive user', function registerSuite() { const inactiveUsers = new InactiveUsers(this.users); await Promise.delay(1000); - await inactiveUsers.deleteInactive(1); + await inactiveUsers.cleanUsers(1); const { username } = regUser; await this.dispatch('users.getInternalData', { username }) .reflect() .then(inspectPromise(false)); }); + + it('removes org member if user not passed activation', async () => { + const opts = { + organizationId: this.organization.id, + member: { + email: regUser.username, + firstName: faker.name.firstName(), + lastName: faker.name.lastName(), + }, + }; + + const reqOpts = { + organizationId: this.organization.id, + }; + + await this.dispatch('users.organization.members.add', opts); + const inactiveUsers = new InactiveUsers(this.users); + await Promise.delay(1200); + + await inactiveUsers.cleanUsers(1); + + const members = await this.dispatch('users.organization.members.list', reqOpts); + const { attributes } = members.data; + const membersWithUsername = ld.filter(attributes, (record) => { + return record.id === regUser.username; + }); + expect(membersWithUsername.length).to.be.eq(0); + }); }); From e2a3d025fc7d4efb1642ce47a9940addd6ecd709 Mon Sep 17 00:00:00 2001 From: pajgo Date: Wed, 13 Nov 2019 12:48:29 +0600 Subject: [PATCH 12/13] feat: delete inactive users * fix --- src/utils/user/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/user/user.js b/src/utils/user/user.js index 216e53d81..185f9eaa5 100644 --- a/src/utils/user/user.js +++ b/src/utils/user/user.js @@ -73,7 +73,7 @@ class User { * @returns {Promise} */ async delete(user, throwError = true) { - const { id, username, alias, ...restData } = typeof user === 'object' || await this.getData(user); + const { id, username, alias, ...restData } = typeof user === 'object' ? user : await this.getData(user); const lock = await this.acquireLock(`delete-user-${id}`); if (lock === null) { From 994f25178c1e578db2c072b25ae1a9f9760f2aeb Mon Sep 17 00:00:00 2001 From: pajgo Date: Wed, 13 Nov 2019 13:54:16 +0600 Subject: [PATCH 13/13] feat: delete inactive users * dlock fix --- src/utils/user/inactive-user.js | 15 ++++++++------- src/utils/user/user.js | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/utils/user/inactive-user.js b/src/utils/user/inactive-user.js index 8d5042e35..9e89ebb2b 100644 --- a/src/utils/user/inactive-user.js +++ b/src/utils/user/inactive-user.js @@ -1,5 +1,6 @@ const Promise = require('bluebird'); const assert = require('assert'); +const { LockAcquisitionError } = require('ioredis-lock'); const User = require('./user'); const UserMetadata = require('../metadata/user'); const Organization = require('../organization/organization'); @@ -31,9 +32,7 @@ class InactiveUser { const { dlock } = this.service; return dlock .once(lockName) - .disposer((l) => { - l.release().reflect(); - }); + .catch(LockAcquisitionError, () => null); } /** @@ -61,10 +60,12 @@ class InactiveUser { * @param {Number}userTTL - interval seconds * @returns {Promise | * | Promise} */ - cleanUsersOnce(userTTL) { - return Promise - .using(this.acquireLock('delete-inactive-users'), () => this.cleanUsers(userTTL)) - .catch({ name: 'LockAcquisitionError' }, () => {}); + async cleanUsersOnce(userTTL) { + const lock = await this.acquireLock('delete-inactive-users'); + if (lock === null) return null; + const result = this.cleanUsers(userTTL); + await lock.release(); + return result; } /** diff --git a/src/utils/user/user.js b/src/utils/user/user.js index 185f9eaa5..38aababcb 100644 --- a/src/utils/user/user.js +++ b/src/utils/user/user.js @@ -110,7 +110,7 @@ class User { handlePipeline(pipelineResult); } finally { - lock.release().reflect(); + lock.release(); } return id;