diff --git a/ReadMe.md b/ReadMe.md index 0dbf820..47df2f7 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -59,11 +59,9 @@ The following parameters can be set in config files or in env variables: - DYNAMODB.AWS_WRITE_UNITS: The DynamoDB table write unit configuration, default is 2 - DYNAMODB.TIMEOUT: The timeout setting used in health check - SCOPES: The M2M scopes, refer `config/default.js` for more information -- ES.HOST: Elasticsearch host, default value is 'localhost:9200' -- ES.API_VERSION: Elasticsearch API version, default value is '6.8' -- ES.ES_INDEX: Elasticsearch index name for resources, default value is 'resources' -- ES.ES_TYPE: Elasticsearch index type for resources, default value is '_doc' -- ES.ES_REFRESH: Elasticsearch force refresh flag, default value is 'true' +- OS.HOST: Opensearch host, default value is 'localhost:9200' +- OS.OS_INDEX: Opensearch index name for resources, default value is 'resources' +- OS.OS_REFRESH: Opensearch force refresh flag, default value is 'true' - BUSAPI_URL: the bus api, default value is 'https://api.topcoder-dev.com/v5' - KAFKA_ERROR_TOPIC: Kafka error topic, default value is 'common.error.reporting', - KAFKA_MESSAGE_ORIGINATOR: the Kafka message originator, default value is 'resources-api' diff --git a/config/default.js b/config/default.js index d03f589..3450fa1 100644 --- a/config/default.js +++ b/config/default.js @@ -44,13 +44,11 @@ module.exports = { TIMEOUT: process.env.DYNAMODB_TIMEOUT || 10000 }, - ES: { - // above AWS_REGION is used if we use AWS ES - HOST: process.env.ES_HOST || 'localhost:9200', - API_VERSION: process.env.ES_API_VERSION || '6.8', - ES_INDEX: process.env.ES_INDEX || 'resources', - ES_TYPE: process.env.ES_TYPE || '_doc', // ES 6.x accepts only 1 Type per index and it's mandatory to define it - ES_REFRESH: process.env.ES_REFRESH || 'true' + OS: { + // above AWS_REGION is used if we use AWS OS + HOST: process.env.OS_HOST || 'localhost:9200', + OS_INDEX: process.env.OS_INDEX || 'resources', + OS_REFRESH: process.env.OS_REFRESH || 'true' }, SCOPES: { diff --git a/package.json b/package.json index 7088bc3..1999299 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "config": "^3.0.1", "cors": "^2.7.1", "dynamoose": "^1.7.2", - "elasticsearch": "^16.1.1", + "@opensearch-project/opensearch": "^2.11.0", "express": "^4.16.4", "express-interceptor": "^1.2.0", "get-parameter-names": "^0.3.0", diff --git a/src/common/helper.js b/src/common/helper.js index 17905bd..e19df81 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -13,14 +13,14 @@ const errors = require('./errors') const logger = require('./logger') const m2mAuth = require('tc-core-library-js').auth.m2m const AWS = require('aws-sdk') -const elasticsearch = require('elasticsearch') +const opensearch = require('@opensearch-project/opensearch') const m2m = m2mAuth(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_PROXY_SERVER_URL'])) const busApi = require('tc-bus-api-wrapper') const busApiClient = busApi(_.pick(config, ['AUTH0_URL', 'AUTH0_AUDIENCE', 'TOKEN_CACHE_TIME', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET', 'BUSAPI_URL', 'KAFKA_ERROR_TOPIC', 'AUTH0_PROXY_SERVER_URL'])) -// Elasticsearch client -let esClient +// Opensearch client +let osClient /** * Check the error is custom error. @@ -436,32 +436,29 @@ async function getAllPages (url, query) { } /** - * Get ES Client - * @return {Object} Elasticsearch Client Instance + * Get OS Client + * @return {Object} Opensearch Client Instance */ -function getESClient () { - if (esClient) { - return esClient +function getOSClient () { + if (osClient) { + return osClient } - const esHost = config.get('ES.HOST') + const osHost = config.get('OS.HOST') // AWS ES configuration is different from other providers - if (/.*amazonaws.*/.test(esHost)) { - esClient = elasticsearch.Client({ - apiVersion: config.get('ES.API_VERSION'), - hosts: esHost, - connectionClass: require('http-aws-es'), // eslint-disable-line global-require + if (/.*amazonaws.*/.test(osHost)) { + osClient = new opensearch.Client({ + node: osHost, amazonES: { region: config.get('DYNAMODB.AWS_REGION'), credentials: new AWS.EnvironmentCredentials('AWS') } }) } else { - esClient = new elasticsearch.Client({ - apiVersion: config.get('ES.API_VERSION'), - hosts: esHost + osClient = new opensearch.Client({ + node: osHost }) } - return esClient + return osClient } /** @@ -539,7 +536,7 @@ module.exports = { isCustomError, setResHeaders, getAllPages, - getESClient, + getOSClient, checkAgreedTerms, postRequest, getMemberById, diff --git a/src/init-es.js b/src/init-es.js index 22f8812..48bc6df 100644 --- a/src/init-es.js +++ b/src/init-es.js @@ -10,26 +10,26 @@ const config = require('config') const logger = require('./common/logger') const helper = require('./common/helper') -const client = helper.getESClient() +const client = helper.getOSClient() const initES = async () => { if (process.argv.length === 3 && process.argv[2] === 'force') { - logger.info(`Delete index ${config.ES.ES_INDEX} if any.`) + logger.info(`Delete index ${config.OS.OS_INDEX} if any.`) try { - await client.indices.delete({ index: config.ES.ES_INDEX }) + await client.indices.delete({ index: config.OS.OS_INDEX }) } catch (err) { // ignore } } - const exists = await client.indices.exists({ index: config.ES.ES_INDEX }) + const exists = await client.indices.exists({ index: config.OS.OS_INDEX }) if (exists) { - logger.info(`The index ${config.ES.ES_INDEX} exists.`) + logger.info(`The index ${config.OS.OS_INDEX} exists.`) } else { - logger.info(`The index ${config.ES.ES_INDEX} will be created.`) + logger.info(`The index ${config.OS.OS_INDEX} will be created.`) const body = { mappings: {} } - body.mappings[config.get('ES.ES_TYPE')] = { + body.mappings['_doc'] = { properties: { id: { type: 'keyword' }, memberHandle: { @@ -40,7 +40,7 @@ const initES = async () => { } await client.indices.create({ - index: config.ES.ES_INDEX, + index: config.OS.OS_INDEX, body }) } diff --git a/src/services/CleanUpService.js b/src/services/CleanUpService.js index bd93ede..76f1458 100644 --- a/src/services/CleanUpService.js +++ b/src/services/CleanUpService.js @@ -9,16 +9,15 @@ const helper = require('../common/helper') const logger = require('../common/logger') /** - * Delete the Resource from the ES by the given id + * Delete the Resource from the OS by the given id * @param id the resource id * @returns {Promise} */ -const deleteFromESById = async (id) => { +const deleteFromOSById = async (id) => { // delete from ES - const esClient = await helper.getESClient() - await esClient.delete({ - index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, + const osClient = await helper.getOSClient() + await osClient.delete({ + index: config.OS.OS_INDEX, id: id, refresh: 'true' // refresh ES so that it is effective for read operations instantly }) @@ -86,7 +85,7 @@ const cleanUpTestData = async () => { for (const res of resources) { logger.info('Resource to be deleted', res.id) await deleteFromDBById('Resource', res.id) - await deleteFromESById(res.id) + await deleteFromOSById(res.id) } logger.info('ResourceRole to be deleted', roleId) await deleteFromDBById('ResourceRole', roleId) diff --git a/src/services/ResourceService.js b/src/services/ResourceService.js index 0a97f80..7ba0af9 100644 --- a/src/services/ResourceService.js +++ b/src/services/ResourceService.js @@ -139,7 +139,8 @@ async function getResources (currentUser, challengeId, roleId, memberId, memberH }) const sortCriteria = [{ [sortBy]: { 'order': sortOrder } }] - const docs = await searchES(mustQuery, perPage, page, sortCriteria) + let docs = await searchOS(mustQuery, perPage, page, sortCriteria) + docs = docs.body // Extract data from hits const allResources = _.map(docs.hits.hits, item => item._source) @@ -374,11 +375,10 @@ async function createResource (currentUser, resource) { createdBy: currentUser.handle || currentUser.sub }, resource)) - // Create resources in ES - const esClient = await helper.getESClient() - await esClient.create({ - index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, + // Create resources in OS + const osClient = await helper.getOSClient() + await osClient.index({ + index: config.OS.OS_INDEX, id: ret.id, body: _.pick(ret, payloadFields), refresh: 'true' // refresh ES so that it is visible for read operations instantly @@ -463,13 +463,12 @@ async function deleteResource (currentUser, resource) { await ret.delete() - // delete from ES - const esClient = await helper.getESClient() - await esClient.delete({ - index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, + // delete from OS + const osClient = await helper.getOSClient() + await osClient.delete({ + index: config.OS.OS_INDEX, id: ret.id, - refresh: 'true' // refresh ES so that it is effective for read operations instantly + refresh: 'true' // refresh OS so that it is effective for read operations instantly }) logger.debug(`Deleted resource, posting to Bus API: ${JSON.stringify(_.pick(ret, payloadFields))}`) @@ -521,17 +520,18 @@ async function listChallengesByMember (memberId, criteria) { } if (criteria.useScroll) { - docs = await searchESWithScroll(mustQuery) + docs = await searchOSWithScroll(mustQuery) } else if (perPage * page <= config.MAX_ELASTIC_SEARCH_RECORDS_SIZE) { - docs = await searchES(mustQuery, perPage, page) + docs = await searchOS(mustQuery, perPage, page).body } else { throw new errors.BadRequestError(` - ES pagination params: + OS pagination params: page ${page}, perPage: ${perPage} exceeds the max search window:${config.MAX_ELASTIC_SEARCH_RECORDS_SIZE}` ) } + logger.debug(`Docs from OS: ${JSON.stringify(docs)}`) // Extract data from hits let result = _.map(docs.hits.hits, item => item._source) @@ -554,11 +554,10 @@ listChallengesByMember.schema = { }).required() } -async function searchESWithScroll (mustQuery) { +async function searchOSWithScroll (mustQuery) { const scrollTimeout = '1m' - const esQuery = { - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), + const osQuery = { + index: config.get('OS.OS_INDEX'), size: 10000, body: { query: { @@ -570,18 +569,17 @@ async function searchESWithScroll (mustQuery) { scroll: scrollTimeout } - const esClient = await helper.getESClient() - const searchResponse = await esClient.search(esQuery) - + const osClient = await helper.getOSClient() + let searchResponse = await osClient.search(osQuery) // eslint-disable-next-line camelcase - const { _scroll_id, hits } = searchResponse + const { _scroll_id, hits } = searchResponse.body const totalHits = hits.total // eslint-disable-next-line camelcase let scrollId = _scroll_id while (hits.hits.length < totalHits) { - const nextScrollResponse = await esClient.scroll({ + const nextScrollResponse = await osClient.scroll({ scroll: scrollTimeout, scroll_id: scrollId }) @@ -590,7 +588,7 @@ async function searchESWithScroll (mustQuery) { hits.hits = [...hits.hits, ...nextScrollResponse.hits.hits] } - await esClient.clearScroll({ + await osClient.clearScroll({ body: { // eslint-disable-next-line camelcase scroll_id: [_scroll_id] @@ -606,18 +604,17 @@ async function searchESWithScroll (mustQuery) { } /** - * Execute ES query + * Execute OS query * @param {Object} mustQuery the query that will be sent to ES * @param {Number} perPage number of search result per page * @param {Number} page the current page - * @returns {Object} doc from ES + * @returns {Object} doc from OS */ -async function searchES (mustQuery, perPage, page, sortCriteria) { - let esQuery +async function searchOS (mustQuery, perPage, page, sortCriteria) { + let osQuery if (sortCriteria) { - esQuery = { - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), + osQuery = { + index: config.get('OS.OS_INDEX'), size: perPage, from: perPage * (page - 1), // Es Index starts from 0 body: { @@ -630,9 +627,8 @@ async function searchES (mustQuery, perPage, page, sortCriteria) { } } } else { - esQuery = { - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), + osQuery = { + index: config.get('OS.OS_INDEX'), size: perPage, from: perPage * (page - 1), // Es Index starts from 0 body: { @@ -644,11 +640,11 @@ async function searchES (mustQuery, perPage, page, sortCriteria) { } } } - logger.debug(`ES Query ${JSON.stringify(esQuery)}`) - const esClient = await helper.getESClient() + logger.debug(`OS Query ${JSON.stringify(osQuery)}`) + const osClient = await helper.getOSClient() let docs try { - docs = await esClient.search(esQuery) + docs = await osClient.search(osQuery) } catch (e) { // Catch error when the ES is fresh and has no data logger.info(`Query Error from ES ${JSON.stringify(e)}`) @@ -675,9 +671,8 @@ async function getResourceCount (challengeId, roleId) { must.push({ term: { 'roleId.keyword': roleId } }) } - const esQuery = { - index: config.get('ES.ES_INDEX'), - type: config.get('ES.ES_TYPE'), + const osQuery = { + index: config.get('OS.OS_INDEX'), size: 0, body: { query: { @@ -695,10 +690,11 @@ async function getResourceCount (challengeId, roleId) { } } - const esClient = await helper.getESClient() + const osClient = await helper.getOSClient() let result try { - result = await esClient.search(esQuery) + result = await osClient.search(osQuery) + result = result.body } catch (err) { logger.error(`Get Resource Count Error ${JSON.stringify(err)}`) throw err diff --git a/test/common/testHelper.js b/test/common/testHelper.js index b35ea68..f68448e 100644 --- a/test/common/testHelper.js +++ b/test/common/testHelper.js @@ -170,13 +170,12 @@ async function clearDependencies () { } /** - * Clear the ES documents. + * Clear the OS documents. */ async function initES () { - const client = helper.getESClient() + const client = helper.getOSClient() await client.deleteByQuery({ - index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, + index: config.OS.OS_INDEX, body: { query: { match_all: {} diff --git a/test/unit/createResource.test.js b/test/unit/createResource.test.js index 4f69554..26708d3 100644 --- a/test/unit/createResource.test.js +++ b/test/unit/createResource.test.js @@ -126,9 +126,8 @@ module.exports = describe('Create resource', () => { describe('create resource - other cases', async () => { it('create resource - task already assign', async () => { const resourceId = uuid() - await helper.getESClient().create({ - index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, + await helper.getOSClient().create({ + index: config.OS.OS_INDEX, id: resourceId, body: { id: resourceId, @@ -147,9 +146,8 @@ module.exports = describe('Create resource', () => { should.equal(err.name, 'ConflictError') assertError(err, 'The Task is already assigned') } finally { - await helper.getESClient().delete({ - index: config.ES.ES_INDEX, - type: config.ES.ES_TYPE, + await helper.getOSClient().delete({ + index: config.OS.OS_INDEX, id: resourceId, refresh: 'true' }) diff --git a/test/unit/edgeCasesForResourceService.test.js b/test/unit/edgeCasesForResourceService.test.js index 1a94ce1..5fcc064 100644 --- a/test/unit/edgeCasesForResourceService.test.js +++ b/test/unit/edgeCasesForResourceService.test.js @@ -13,7 +13,7 @@ const challengeId = 'fe6d0a58-ce7d-4521-8501-b8132b1c0391' module.exports = describe('Edge cases for resource service', () => { before(async () => { try { - await helper.getESClient().indices.delete({ index: config.ES.ES_INDEX }) + await helper.getOSClient().indices.delete({ index: config.OS.OS_INDEX }) } catch (err) { // ignore } @@ -21,15 +21,15 @@ module.exports = describe('Edge cases for resource service', () => { after(async () => { const body = { mappings: {} } - body.mappings[config.get('ES.ES_TYPE')] = { + body.mappings['_doc'] = { properties: { id: { type: 'keyword' } } } try { - await helper.getESClient().indices.create({ - index: config.ES.ES_INDEX, + await helper.getOSClient().indices.create({ + index: config.OS.OS_INDEX, body }) } catch (err) { @@ -37,12 +37,12 @@ module.exports = describe('Edge cases for resource service', () => { } }) - it('get resources by admin - ES is fresh', async () => { + it('get resources by admin - OS is fresh', async () => { const result = await service.getResources(user.admin, challengeId) should.equal(result.total, 0) }) - it('get challenges hohosky can access - ES is fresh', async () => { + it('get challenges hohosky can access - OS is fresh', async () => { const ret = await service.listChallengesByMember('16096823', {}) should.equal(ret.data.length, 0) }) diff --git a/yarn.lock b/yarn.lock index ea1cae3..b6a0e4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -167,6 +167,18 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@opensearch-project/opensearch@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@opensearch-project/opensearch/-/opensearch-2.11.0.tgz#e5539b49d9ccbc746d023f4f5f89b64501332d16" + integrity sha512-G+SZwtWRDv90IrtTSNnCt0MQjHVyqrcIXcpwN68vjHnfbun2+RHn+ux4K7dnG+s/KwWzVKIpPFoRjg2gfFX0Mw== + dependencies: + aws4 "^1.11.0" + debug "^4.3.1" + hpagent "^1.2.0" + json11 "^1.1.2" + ms "^2.1.3" + secure-json-parse "^2.4.0" + "@postman/form-data@~3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@postman/form-data/-/form-data-3.1.1.tgz#d0446d0d3639a291f5e800e89fa1d0d3723f9414" @@ -337,13 +349,6 @@ agent-base@6: dependencies: debug "4" -agentkeepalive@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" - integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== - dependencies: - humanize-ms "^1.2.1" - ajv-keywords@^3.0.0: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -522,6 +527,11 @@ aws4@1.11.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +aws4@^1.11.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.1.tgz#bb5f8b8a20739f6ae1caeaf7eea2c7913df8048e" + integrity sha512-u5w79Rd7SU4JaIlA/zFqG+gOiuq25q5VLyZ8E+ijJeILuTxVzZgp2CaGw/UTw6pXYN9XMO9yiqj/nEHmhTG5CA== + aws4@^1.8.0: version "1.12.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" @@ -738,7 +748,7 @@ chai@^4.1.2: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== @@ -1069,6 +1079,13 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.3.1: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1213,15 +1230,6 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -elasticsearch@^16.1.1: - version "16.7.3" - resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-16.7.3.tgz#bf0e1cc129ab2e0f06911953a1b1f3c740715fab" - integrity sha512-e9kUNhwnIlu47fGAr4W6yZJbkpsgQJB0TqNK8rCANe1J4P65B1sGnbCFTgcKY3/dRgCWnuP1AJ4obvzW604xEQ== - dependencies: - agentkeepalive "^3.4.1" - chalk "^1.0.0" - lodash "^4.17.10" - emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -2065,6 +2073,11 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hpagent@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-1.2.0.tgz#0ae417895430eb3770c03443456b8d90ca464903" + integrity sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -2149,13 +2162,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - iconv-lite@0.4.24, iconv-lite@^0.4.17: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2579,6 +2585,11 @@ json-stringify-safe@5.0.1, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0. resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +json11@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/json11/-/json11-1.1.2.tgz#35ffd3ee5073b0cc09ef826b0a0dc005ebef2b5b" + integrity sha512-5r1RHT1/Gr/jsI/XZZj/P6F11BKM8xvTaftRuiLkQI9Z2PFDukM82Ysxw8yDszb3NJP/NKnRlSGmhUdG99rlBw== + json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -2770,7 +2781,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -2998,7 +3009,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.2: +ms@2.1.3, ms@^2.1.1, ms@^2.1.2, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -3917,6 +3928,11 @@ sax@>=0.6.0: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + "semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"