diff --git a/lib/api/middlewares/thumbnail-proxy.js b/lib/api/middlewares/thumbnail-proxy.js index a465fef..1461dac 100644 --- a/lib/api/middlewares/thumbnail-proxy.js +++ b/lib/api/middlewares/thumbnail-proxy.js @@ -1,9 +1,10 @@ const {join} = require('path') -const got = require('got') const fileType = require('file-type') const fresh = require('fresh') const cacheControl = require('@tusbar/cache-control') +const got = require('../../got') + const defaultThumbnailPath = join(__dirname, '../../../france/img/unavailable-thumbnail.png') const ACCEPTED_MIME_TYPES = [ diff --git a/lib/api/routes/links.js b/lib/api/routes/links.js index 36d92e1..11cd473 100644 --- a/lib/api/routes/links.js +++ b/lib/api/routes/links.js @@ -2,9 +2,9 @@ const {createWriteStream} = require('fs') const {join} = require('path') const {Router} = require('express') const unzipper = require('unzipper') -const got = require('got') const pump = require('pump') +const got = require('../../got') const {getLink} = require('../../services/link-proxy') const {createTempDirectory} = require('../../utils/tempfile') const ogr2ogr = require('../../extract/ogr2ogr') diff --git a/lib/got.js b/lib/got.js new file mode 100644 index 0000000..8d0a1ff --- /dev/null +++ b/lib/got.js @@ -0,0 +1,9 @@ +const got = require('got') + +const pkg = require('../package.json') + +module.exports = got.extend({ + headers: { + 'user-agent': `geoplatform/${pkg.version} (+https://geo.data.gouv.fr)` + } +}) diff --git a/lib/services/link-proxy.js b/lib/services/link-proxy.js index 89a20bb..d43b58b 100644 --- a/lib/services/link-proxy.js +++ b/lib/services/link-proxy.js @@ -1,6 +1,6 @@ 'use strict' -const got = require('got') +const got = require('../got') const {LINK_PROXY_URL} = process.env diff --git a/package.json b/package.json index 9ce141b..2f105bf 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "express-session": "^1.15.6", "file-type": "^10.2.0", "fresh": "^0.5.2", - "got": "^9.2.2", + "got": "^9.3.0", "handlebars": "^4.0.12", "hasha": "^3.0.0", "ipaddr.js": "^1.8.1", @@ -51,7 +51,6 @@ "pump": "^3.0.0", "raven": "^2.6.4", "stream-each": "^1.2.3", - "superagent": "^3.5.2", "through2": "^2.0.3", "tldjs": "^2.0.0", "underscore.string": "^3.3", diff --git a/plugins/publish-to-udata/app.js b/plugins/publish-to-udata/app.js index 60ac67a..960d225 100644 --- a/plugins/publish-to-udata/app.js +++ b/plugins/publish-to-udata/app.js @@ -54,7 +54,7 @@ module.exports = function () { req.session.redirectTo = undefined }) - app.use('/proxy-api', require('./udataProxy')()) + app.use('/proxy-api', require('./udata-proxy')) app.use('/api', require('./routes/producers')()) app.use('/api', require('./routes/organizations')()) diff --git a/plugins/publish-to-udata/geogw.js b/plugins/publish-to-udata/geogw.js index 8b0f202..aa3f3c5 100644 --- a/plugins/publish-to-udata/geogw.js +++ b/plugins/publish-to-udata/geogw.js @@ -1,6 +1,6 @@ 'use strict' -const got = require('got') +const got = require('./got') const ROOT_URL = process.env.GEOGW_URL + '/api/geogw' const TOKEN = process.env.GEOGW_TOKEN diff --git a/plugins/publish-to-udata/got.js b/plugins/publish-to-udata/got.js new file mode 100644 index 0000000..10d7676 --- /dev/null +++ b/plugins/publish-to-udata/got.js @@ -0,0 +1,7 @@ +const got = require('got') + +module.exports = got.extend({ + headers: { + 'user-agent': 'geoplatform/udata-publisher/v1.0.0 (+https://geo.data.gouv.fr)' + } +}) diff --git a/plugins/publish-to-udata/udata-proxy.js b/plugins/publish-to-udata/udata-proxy.js new file mode 100644 index 0000000..684c45a --- /dev/null +++ b/plugins/publish-to-udata/udata-proxy.js @@ -0,0 +1,38 @@ +const {Router} = require('express') + +const got = require('./got') + +const ALLOWED_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'] +const baseUrl = process.env.DATAGOUV_URL + '/api' + +const router = new Router({ + strict: true +}) + +router.all('*', (req, res, next) => { + if (!ALLOWED_METHODS.includes(req.method)) { + return res.status(405).send() + } + + const options = { + baseUrl, + body: req.body, + method: req.method, + throwHttpErrors: false + } + + if (req.user) { + options.headers = { + authorization: `Bearer ${req.user.accessToken}` + } + } + + got + .stream(req.path, options) + .on('error', error => { + next(error) + }) + .pipe(res) +}) + +module.exports = router diff --git a/plugins/publish-to-udata/udata.js b/plugins/publish-to-udata/udata.js index 094f734..968d702 100644 --- a/plugins/publish-to-udata/udata.js +++ b/plugins/publish-to-udata/udata.js @@ -1,138 +1,195 @@ -'use strict' +const got = require('./got') -const request = require('superagent') -const Promise = require('bluebird') +const {DATAGOUV_URL, UDATA_PUBLICATION_USER_API_KEY} = process.env -const rootUrl = process.env.DATAGOUV_URL + '/api/1' -const apiKey = process.env.UDATA_PUBLICATION_USER_API_KEY +const client = got.extend({ + baseUrl: `${DATAGOUV_URL}/api/1` +}) -function withApiKey(req) { - return req.set('X-API-KEY', apiKey) +async function getProfile(accessToken) { + const {body} = await client.get(`/me/`, { + json: true, + headers: { + authorization: `Bearer ${accessToken}` + } + }) + + return body } -function withToken(req, token) { - return req.set('Authorization', `Bearer ${token}`) +async function getOrganization(organizationId) { + const {body} = await client.get(`/organizations/${organizationId}/`, { + json: true + }) + + return body } -function getProfile(accessToken) { - return Promise.resolve( - withToken(request.get(rootUrl + '/me/'), accessToken) - .then(resp => resp.body) - ) +async function addUserToOrganization(userId, organizationId, accessToken) { + try { + await client.post(`/organizations/${organizationId}/member/${userId}/`, { + json: true, + headers: { + authorization: `Bearer ${accessToken}` + }, + body: { + role: 'admin' + } + }) + } catch (error) { + if (error.statusCode === 409) { + // User is already a member, ignoring + return + } + + throw error + } } -function getOrganization(organizationId) { - return Promise.resolve( - request.get(`${rootUrl}/organizations/${organizationId}/`) - .then(resp => resp.body) - ) +async function removeUserFromOrganization(userId, organizationId, accessToken) { + await client.delete(`/organizations/${organizationId}/member/${userId}/`, { + headers: { + authorization: `Bearer ${accessToken}`, + 'content-length': 0 + } + }) } -function addUserToOrganization(userId, organizationId, accessToken) { - return Promise.resolve( - withToken(request.post(`${rootUrl}/organizations/${organizationId}/member/${userId}`), accessToken) - .send({role: 'admin'}) - .catch(err => { - if (err.status && err.status === 409) { - return - } // User is already member - throw err - }) +async function getUserRoleInOrganization(userId, organizationId) { + const organization = await getOrganization(organizationId) + + const membership = organization.members.find( + membership => membership.user.id === userId ) -} -function removeUserFromOrganization(userId, organizationId, accessToken) { - return Promise.resolve( - withToken(request.del(`${rootUrl}/organizations/${organizationId}/member/${userId}`), accessToken) - .set('content-length', 0) - ).thenReturn() + return membership ? membership.role : 'none' } -function getUserRoleInOrganization(userId, organizationId) { - return getOrganization(organizationId) - .then(organization => { - const membership = organization.members.find(membership => membership.user.id === userId) - return membership ? membership.role : 'none' - }) +async function deleteDatasetResource(datasetId, resourceId) { + await client.delete(`/datasets/${datasetId}/resources/${resourceId}/`, { + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY, + 'content-length': 0 + } + }) } -function deleteDatasetResource(datasetId, resourceId) { - return Promise.resolve( - withApiKey(request.del(rootUrl + '/datasets/' + datasetId + '/resources/' + resourceId + '/')) - .set('content-length', 0) - ).thenReturn() -} +async function createDataset(dataset) { + const {body} = await client.post('/datasets/', { + json: true, + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY + }, + body: dataset + }) -function createDataset(dataset) { - return Promise.resolve( - withApiKey(request.post(rootUrl + '/datasets/')) - .send(dataset) - .then(resp => resp.body) - ) + return body } -function updateDataset(datasetId, dataset) { - const updateOnly = Promise.resolve( - withApiKey(request.put(rootUrl + '/datasets/' + datasetId + '/')) - .send(dataset) - .then(resp => resp.body) - ) +async function updateDataset(datasetId, dataset) { + const {body} = await client.put(`/datasets/${datasetId}/`, { + json: true, + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY + }, + body: dataset + }) + if (dataset.resources.length > 0) { - return updateOnly + return body } - return updateOnly.then(publishedDataset => { - return Promise.each(publishedDataset.resources, resource => deleteDatasetResource(datasetId, resource.id)) - .then(() => getDataset(datasetId)) - }) -} -function getDataset(datasetId) { - return Promise.resolve( - withApiKey(request.get(rootUrl + '/datasets/' + datasetId + '/')) - .then(resp => resp.body) + await Promise.all( + body.resources.map(resource => deleteDatasetResource(datasetId, resource.id)) ) + + return getDataset(datasetId) } -function deleteDataset(datasetId) { - return Promise.resolve( - withApiKey(request.del(rootUrl + '/datasets/' + datasetId + '/')) - .set('content-length', 0) - ).thenReturn() +async function getDataset(datasetId) { + const {body} = await client.get(`/datasets/${datasetId}/`, { + json: true, + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY + } + }) + + return body } -function createDatasetTransferRequest(datasetId, recipientOrganizationId) { - return Promise.resolve( - withApiKey(request.post(rootUrl + '/transfer/')) - .send({ - subject: {id: datasetId, class: 'Dataset'}, - recipient: {id: recipientOrganizationId, class: 'Organization'}, - comment: 'INSPIRE gateway automated transfer: request' - }) - .then(resp => resp.body.id) - ) +async function deleteDataset(datasetId) { + await client.delete(`/datasets/${datasetId}/`, { + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY, + 'content-length': 0 + } + }) } -function respondTransferRequest(transferId, response = 'accept') { - return Promise.resolve( - withApiKey(request.post(`${rootUrl}/transfer/${transferId}/`)) - .send({comment: 'INSPIRE gateway automated transfer: response', response}) - ).thenReturn() +async function createDatasetTransferRequest(datasetId, recipientOrganizationId) { + const {body} = await client.post('/transfer/', { + json: true, + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY + }, + body: { + subject: { + id: datasetId, + class: 'Dataset' + }, + recipient: { + id: recipientOrganizationId, + class: 'Organization' + }, + comment: 'INSPIRE gateway automated transfer: request' + } + }) + + return body.id } -function transferDataset(datasetId, recipientOrganizationId) { - return getDataset(datasetId) - .catch(err => { - if (err.status && err.status === 404) { - throw new Error('Dataset doesn\'t exist') - } - }) - .then(() => createDatasetTransferRequest(datasetId, recipientOrganizationId)) - .then(transferId => respondTransferRequest(transferId, 'accept')) - .catch(err => { - if (err.status && err.status === 400 && err.response.body.message === 'Recipient should be different than the current owner') { +async function respondTransferRequest(transferId, response = 'accept') { + await client.post(`/transfer/${transferId}/`, { + json: true, + headers: { + 'X-API-KEY': UDATA_PUBLICATION_USER_API_KEY + }, + body: { + comment: 'INSPIRE gateway automated transfer: response', + response + } + }) +} - } - }) +async function transferDataset(datasetId, recipientOrganizationId) { + try { + await getDataset(datasetId) + } catch (error) { + throw new Error('Dataset doesn’t exist') + } + + try { + const transferId = await createDatasetTransferRequest(datasetId, recipientOrganizationId) + + await respondTransferRequest(transferId) + } catch (error) { + if (error.statusCode === 400 && error.body.message === 'Recipient should be different than the current owner') { + // Swallow this error… + } else { + throw error + } + } } -module.exports = {getOrganization, addUserToOrganization, removeUserFromOrganization, getProfile, createDataset, updateDataset, deleteDataset, getDataset, getUserRoleInOrganization, transferDataset} +module.exports = { + getOrganization, + addUserToOrganization, + removeUserFromOrganization, + getProfile, + createDataset, + updateDataset, + deleteDataset, + getDataset, + getUserRoleInOrganization, + transferDataset +} diff --git a/plugins/publish-to-udata/udataProxy.js b/plugins/publish-to-udata/udataProxy.js deleted file mode 100644 index d828b1b..0000000 --- a/plugins/publish-to-udata/udataProxy.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict' - -const express = require('express') -const request = require('superagent') - -const rootUrl = process.env.DATAGOUV_URL + '/api' - -const ALLOWED_METHODS = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'] - -module.exports = function () { - const router = new express.Router({strict: true}) - - router.all('*', (req, res, next) => { - if (!ALLOWED_METHODS.includes(req.method)) { - return res.sendStatus(405) - } - const method = req.method.toLowerCase() - const url = rootUrl + req.path - const proxyReq = request[method](url).query(req.query) - if (req.user) { - proxyReq.set('Authorization', 'Bearer ' + req.user.accessToken) - } - if (req.body) { - proxyReq.send(req.body) - } - proxyReq.end((err, proxyRes) => { - if (err && !err.status) { - return next(err) - } - if (err && err.status) { - proxyRes = err.response - } - res.status(proxyRes.status) - if (proxyRes.body) { - res.send(proxyRes.body) - } else { - res.end() - } - }) - }) - - return router -} diff --git a/yarn.lock b/yarn.lock index ff51b4c..f92c7c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,6 +38,13 @@ dependencies: symbol-observable "^1.2.0" +"@sindresorhus/is@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.12.0.tgz#55c37409c809e802efea25911a579731adfc6e07" + integrity sha512-9ve22cGrAKlSRvi8Vb2JIjzcaaQg79531yQHnF+hi/kOpsSj3Om8AyR1wcHrgl0u7U3vYQ7gmF5erZzOp4+51Q== + dependencies: + symbol-observable "^1.2.0" + "@szmarczak/http-timer@^1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.1.tgz#6402258dfe467532b26649ef076b4d11f74fb612" @@ -2006,6 +2013,19 @@ cacheable-request@^5.0.0: normalize-url "^3.1.0" responselike "^1.0.2" +cacheable-request@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-5.1.0.tgz#ce0958e977bdb4a5b718464049793b8d4bf7d75d" + integrity sha512-UCdjX4N/QjymZGpKY7hW4VJsxsVJM+drIiCxPa9aTvFQN5sL2+kJCYyeys8f2W0dJ0sU6Et54Ovl0sAmCpHHsA== + dependencies: + clone-response "^1.0.2" + get-stream "^4.0.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^1.0.1" + normalize-url "^3.1.0" + responselike "^1.0.2" + call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" @@ -2306,7 +2326,7 @@ commander@~2.17.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -component-emitter@^1.2.0, component-emitter@^1.2.1: +component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= @@ -2413,11 +2433,6 @@ cookie@0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= -cookiejar@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" - integrity sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o= - copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -3440,11 +3455,6 @@ extend@^1.2.1: resolved "https://registry.yarnpkg.com/extend/-/extend-1.3.0.tgz#d1516fb0ff5624d2ebf9123ea1dac5a1994004f8" integrity sha1-0VFvsP9WJNLr+RI+odrFoZlABPg= -extend@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= - extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -3660,7 +3670,7 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= -form-data@^2.3.1, form-data@~2.3.2: +form-data@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" integrity sha1-SXBJi+YEwgwAXU9cI67NIda0kJk= @@ -3669,11 +3679,6 @@ form-data@^2.3.1, form-data@~2.3.2: combined-stream "1.0.6" mime-types "^2.1.12" -formidable@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" - integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg== - forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -3816,6 +3821,13 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -3978,6 +3990,23 @@ got@^9.2.2: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" +got@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.3.0.tgz#9187472a6e7c642264d041b0e670fd8bb1eebb67" + integrity sha512-4WTeObCRe7hatjQmeCwmkviu+ibyfeF5v6De+FeZdfsLEIe/7d9rl3VOzrknXveeQV/Pq/+f+KbscBQsP8ZxUg== + dependencies: + "@sindresorhus/is" "^0.12.0" + "@szmarczak/http-timer" "^1.1.0" + cacheable-request "^5.1.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -5673,7 +5702,7 @@ merge@^1.2.0: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" integrity sha1-dTHjnUlJwoGma4xabgJl6LBYlNo= -methods@^1.1.1, methods@~1.1.2: +methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -5745,11 +5774,6 @@ mime@1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== -mime@^1.4.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -6410,6 +6434,11 @@ p-cancelable@^0.5.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.5.1.tgz#b797a33c43c645cd70d5a838b1d25352b9e29e75" integrity sha512-vkOBXQgQb03QTOoMeeB5/uS2W3iafXzQLaIh7ChHjEb8DDT06sWJizhdOACL1Sittl5dFqsyumJ4rD1WUF8Isw== +p-cancelable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.0.0.tgz#07e9c6d22c31f9c6784cb4f1e1454a79b6d9e2d6" + integrity sha512-USgPoaC6tkTGlS831CxsVdmZmyb8tR1D+hStI84MyckLOzfJlYQUweomrwE3D8T7u5u5GVuW064LT501wHTYYA== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -6930,7 +6959,7 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.5.1, qs@^6.5.1: +qs@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== @@ -7998,22 +8027,6 @@ stylus@0.54.5, stylus@~0.54.5: sax "0.5.x" source-map "0.1.x" -superagent@^3.5.2: - version "3.8.2" - resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.2.tgz#e4a11b9d047f7d3efeb3bbe536d9ec0021d16403" - integrity sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ== - dependencies: - component-emitter "^1.2.0" - cookiejar "^2.1.0" - debug "^3.1.0" - extend "^3.0.0" - form-data "^2.3.1" - formidable "^1.1.1" - methods "^1.1.1" - mime "^1.4.1" - qs "^6.5.1" - readable-stream "^2.0.5" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"