diff --git a/.gitignore b/.gitignore index e6e3d9b..3dfd0b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules -yarn* build package-lock.json *.swp diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 1a68b60..081a923 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@oada/oada-cache", - "version": "3.1.8", + "version": "4.0.0", "description": "node library for interacting with and locally caching data served on an oada-compliant server", "main": "./src/index.js", "directories": { @@ -9,7 +9,8 @@ }, "mocha": { "exit": true, - "spec": "test/**/*.test.js", + "recursive": true, + "require" : ["mocha-steps"], "file": [ "./test/setup-all-tests.js" ] @@ -42,7 +43,8 @@ }, "devDependencies": { "http-status": "^1.2.0", - "mocha": "^7.0.1" + "mocha": "^7.0.1", + "mocha-steps": "^1.3.0" }, "repository": { "type": "git", diff --git a/src/cache.js b/src/cache.js index d83dc1b..a059e3e 100644 --- a/src/cache.js +++ b/src/cache.js @@ -627,6 +627,32 @@ module.exports = function setupCache({ name, req, expires, dbprefix }) { } } + // Upsert resources using array-based change doc + async function _iterativeUpsert(req, changeArray) { + if (!Array.isArray(changeArray)) { + throw new Error("Body must be an array."); + } + + // Perform upserts by iterating serially over the change array + return Promise.each(changeArray, async change => { + if (change.type === "merge") { + await dbUpsert({ + url: "/" + change.resource_id, + data: change.body + }); + } else if (change.type === "delete") { + var nullPath = await findNullValue(change.body, "", ""); + return dbUpsert({ + url: "/" + change.resource_id + nullPath, + method: "delete", + valid: true + }); + } else { + throw new Error("Unrecognized change type."); + } + }); + } + function findNullValue(obj, path, nullPath) { if (typeof obj === "object") { return Promise.map( @@ -700,52 +726,59 @@ module.exports = function setupCache({ name, req, expires, dbprefix }) { var queue = cq() .limit({ concurrency: 1 }) .process(async function(payload) { - let urlObj = url.parse(payload.request.url); - // Give the change body an _id so the deepest resource can be found - payload.response.change.body = payload.response.change.body || {}; - payload.response.change.body._id = payload.response.resourceId; - //TODO: This should be unnecessary. The payload ought to specify the root - //of the watch as a resource. - return findDeepestResource(payload.response.change.body, "", { - path: "", - data: payload.response.change.body, - }) - .then(async deepestResource => { - if (payload.response.change.wasDelete) { - // DELETE: remove the deepest resource from the change body, execute - // the delete, and recursively update all other revs in the cache - let nullPath = await findNullValue(deepestResource.data, "", ""); - let deletedPath = deepestResource.path + nullPath; - payload.nullPath = deletedPath; - return dbUpsert({ - url: payload.request.url + deletedPath, - headers: payload.request.headers, - method: "delete", - valid: true, - }).then(async function() { - // Update revs on all parents all the way down to (BUT OMITTING) the - // resource on which the delete was called. - //pointer.remove(payload.response.change.body, deepestResource.path || '/') - // await _recursiveUpsert(payload.request, payload.response.change.body) - return payload; - }); - } else { - // Recursively update all of the resources down the returned change body - /* + // FIXME: check OADA version to determine change doc format + if (Array.isArray(payload.response.change)) { + // process change array (new format) + return _iterativeUpsert(payload.request, payload.response.change); + } else { + // for backward compatibility (old format) + let urlObj = url.parse(payload.request.url); + // Give the change body an _id so the deepest resource can be found + payload.response.change.body = payload.response.change.body || {}; + payload.response.change.body._id = payload.response.resourceId; + //TODO: This should be unnecessary. The payload ought to specify the root + //of the watch as a resource. + return findDeepestResource(payload.response.change.body, "", { + path: "", + data: payload.response.change.body + }) + .then(async deepestResource => { + if (payload.response.change.wasDelete) { + // DELETE: remove the deepest resource from the change body, execute + // the delete, and recursively update all other revs in the cache + let nullPath = await findNullValue(deepestResource.data, "", ""); + let deletedPath = deepestResource.path + nullPath; + payload.nullPath = deletedPath; + return dbUpsert({ + url: payload.request.url + deletedPath, + headers: payload.request.headers, + method: "delete", + valid: true + }).then(async function() { + // Update revs on all parents all the way down to (BUT OMITTING) the + // resource on which the delete was called. + //pointer.remove(payload.response.change.body, deepestResource.path || '/') + // await _recursiveUpsert(payload.request, payload.response.change.body) + return payload; + }); + } else { + // Recursively update all of the resources down the returned change body + /* var oldBody = await _recursiveUpsert(payload.request, payload.response.change.body, {}) payload.oldBody = oldBody; return payload; */ - await _recursiveUpsert( - payload.request, - payload.response.change.body.data, - ); + await _recursiveUpsert( + payload.request, + payload.response.change.body.data + ); + return payload; + } + }) + .catch(err => { return payload; - } - }) - .catch(err => { - return payload; - }); + }); + } }); function handleWatchChange(payload) { diff --git a/src/index.js b/src/index.js index 50a6b74..3cf14ce 100644 --- a/src/index.js +++ b/src/index.js @@ -1,428 +1,424 @@ -const Promise = require("bluebird"); -let setupCache = require("./cache"); -const uuid = require("uuid"); -const _ = require("lodash"); -const urlLib = require("url"); -const pointer = require("json-pointer"); -const ws = require("./websocket"); -const axios = require("axios"); -const _TOKEN = require("./token"); +const Promise = require('bluebird') +let setupCache = require('./cache') +const uuid = require('uuid') +const _ = require('lodash') +const urlLib = require('url') +const pointer = require('json-pointer') +const ws = require('./websocket') +const axios = require('axios') +const _TOKEN = require('./token') // debug -const error = require("debug")("oada-cache:index:error"); -const info = require("debug")("oada-cache:index:info"); -const trace = require("debug")("oada-cache:index:trace"); +const error = require('debug')('oada-cache:index:error') +const info = require('debug')('oada-cache:index:info') +const trace = require('debug')('oada-cache:index:trace') -let dbprefix = ""; -const setDbPrefix = pfx => (dbprefix = pfx); +let dbprefix = '' +const setDbPrefix = pfx => (dbprefix = pfx) process.on('unhandledRejection', (reason, p) => { - console.log('------Unhandled Rejection - Fix Me!-------'); - console.log(reason); -}); + console.log('------Unhandled Rejection - Fix Me!-------') + console.log(reason) +}) -function domainToCacheName(domain) { - return urlLib.parse(domain).hostname.replace(/\./g, "_"); +function domainToCacheName (domain) { + return urlLib.parse(domain).hostname.replace(/\./g, '_') } -var connect = async function connect({ +var connect = async function connect ({ domain, options, cache, token, - websocket, + websocket }) { if (!domain) { - throw new Error("domain undefined"); + throw new Error('domain undefined') } - if (typeof domain !== "string") { - throw new Error("domain must be a string"); + if (typeof domain !== 'string') { + throw new Error('domain must be a string') } if (!options && !token) { - throw new Error("options and token undefined"); + throw new Error('options and token undefined') } - if (token && typeof token !== "string") { - throw new Error("token must be a string"); + if (token && typeof token !== 'string') { + throw new Error('token must be a string') } if ( cache !== undefined && - typeof cache !== "boolean" && - typeof cache !== "object" + typeof cache !== 'boolean' && + typeof cache !== 'object' ) { throw new Error( - `cache must be either a boolean or an object with 'name' and/or 'expires' keys`, - ); + `cache must be either a boolean or an object with 'name' and/or 'expires' keys` + ) } - if (cache && cache.name && typeof cache.name !== "string") { - throw new Error("cache name must be a string"); + if (cache && cache.name && typeof cache.name !== 'string') { + throw new Error('cache name must be a string') } // if (typeof cache !== "undefined" && typeof cache !== "boolean") //throw "cache must be boolean"; - if (typeof websocket !== "undefined" && typeof websocket !== "boolean") { - throw new Error("websocket must be boolean"); + if (typeof websocket !== 'undefined' && typeof websocket !== 'boolean') { + throw new Error('websocket must be boolean') } - var CACHE; - var REQUEST = axios; - var NOCACHEREQUEST = axios; - var SOCKET; - var TOKEN; - let _token = new _TOKEN({ domain, token, options, dbprefix }); + var CACHE + var REQUEST = axios + var NOCACHEREQUEST = axios + var SOCKET + var TOKEN + let _token = new _TOKEN({ domain, token, options, dbprefix }) if (!domain) { - throw new Error("domain undefined"); + throw new Error('domain undefined') } - var DOMAIN = domain; - var NAME = - cache && cache.name - ? cache.name - : domainToCacheName(domain); //urlLib.parse(domain).hostname.replace(/\./g, "_"); - var EXPIRES = cache && cache.expires ? cache.expires : undefined; + var DOMAIN = domain + var NAME = cache && cache.name ? cache.name : domainToCacheName(domain) //urlLib.parse(domain).hostname.replace(/\./g, "_"); + var EXPIRES = cache && cache.expires ? cache.expires : undefined - function _replaceLinks(obj) { - let ret = Array.isArray(obj) ? [] : {}; + function _replaceLinks (obj) { + let ret = Array.isArray(obj) ? [] : {} if (!obj) { - return obj; + return obj } // no defined objriptors for this level return Promise.map(Object.keys(obj || {}), key => { - if (key === "*") { + if (key === '*') { // Don't put *s into oada. Ignore them - return; + return } - let val = obj[key]; - if (typeof val !== "object" || !val) { - ret[key] = val; // keep it asntType: 'application/vnd.oada.harvest.1+json' - return; + let val = obj[key] + if (typeof val !== 'object' || !val) { + ret[key] = val // keep it asntType: 'application/vnd.oada.harvest.1+json' + return } if (val._type) { // If it has a '_type' key, don't worry about it. //It'll get created in future iterations of ensureTree - return; + return } if (val._id) { // If it's an object, and has an '_id', make it a link from descriptor - ret[key] = { _id: val._id }; - if ("_rev" in val) { - ret[key]._rev = val._rev; + ret[key] = { _id: val._id } + if ('_rev' in val) { + ret[key]._rev = val._rev } - return; + return } // otherwise, recurse into the object looking for more links return _replaceLinks(val).then(result => { - ret[key] = result; - return; - }); + ret[key] = result + return + }) }).then(() => { - return ret; - }); + return ret + }) } - async function _makeResourceAndLink({ path, data, headers }, waitTime) { - data._id = _.clone(data._id) || "resources/" + uuid(); + async function _makeResourceAndLink ({ path, data, headers }, waitTime) { + data._id = _.clone(data._id) || 'resources/' + uuid() let linkReq = { path, type: data._type, headers, - data: { _id: data._id }, - }; + data: { _id: data._id } + } // Create a versioned link if the tree specifies one. - if ("_rev" in data) { - linkReq.data._rev = 0; + if ('_rev' in data) { + linkReq.data._rev = 0 } // We don't want to attempt to set the rev when we put the resource let resReq = { - path: "/" + data._id, + path: '/' + data._id, type: data._type, - data, - }; - var link; + data + } + var link try { - link = await put(linkReq); + link = await put(linkReq) } catch (err) { if (err.response && err.response.status === 412) { - var pathPieces = path.split("/"); - var parentPath = pathPieces.splice(0, pathPieces.length - 1).join("/"); + var pathPieces = path.split('/') + var parentPath = pathPieces.splice(0, pathPieces.length - 1).join('/') // Wait time increases: 1s, 2s, 4s, 8s, 16s. Throw after 16s. if (waitTime > 16000) { - throw err; + throw err } //The parent has been modified; attempt to get the new _rev - var response; + var response try { response = await NOCACHEREQUEST({ - method: "get", + method: 'get', url: DOMAIN + parentPath, - headers: { Authorization: "Bearer " + TOKEN }, - }); + headers: { Authorization: 'Bearer ' + TOKEN } + }) } catch (erro) { - waitTime = waitTime || 1000; - await Promise.delay(waitTime); - return _makeResourceAndLink({ path, data, headers }, waitTime * 2); + waitTime = waitTime || 1000 + await Promise.delay(waitTime) + return _makeResourceAndLink({ path, data, headers }, waitTime * 2) } // If the key has already been created, set the resource path to the bookmarks path and let it map automatically, not a resource id if (response.data[pathPieces[pathPieces.length - 1]]) { - resReq.data._id = - response.data[pathPieces[pathPieces.length - 1]]._id; - resReq.path = "/" + resReq.data._id; + resReq.data._id = response.data[pathPieces[pathPieces.length - 1]]._id + resReq.path = '/' + resReq.data._id // Concurrent put, delete, put (delete.test.js #15) can produce situations where _id is undefined if (!resReq.data._id) { - waitTime = waitTime || 1000; - await Promise.delay(waitTime); - var newHeaders = _.cloneDeep(headers); + waitTime = waitTime || 1000 + await Promise.delay(waitTime) + var newHeaders = _.cloneDeep(headers) //TODO: use if-match headers??? - newHeaders["if-match"] = parseInt(response.headers["x-oada-rev"]); + newHeaders['if-match'] = parseInt(response.headers['x-oada-rev']) return _makeResourceAndLink( { path, data, headers: newHeaders }, - waitTime * 2, - ); + waitTime * 2 + ) } } else { // The key does not yet exist, adjust the if-match and try again. - waitTime = waitTime || 1000; - await Promise.delay(waitTime); - var newHeaders = _.cloneDeep(headers); - newHeaders["if-match"] = parseInt(response.headers["x-oada-rev"]); + waitTime = waitTime || 1000 + await Promise.delay(waitTime) + var newHeaders = _.cloneDeep(headers) + newHeaders['if-match'] = parseInt(response.headers['x-oada-rev']) return _makeResourceAndLink( { path, data, headers: newHeaders }, - waitTime * 2, - ); + waitTime * 2 + ) } } else { - throw err; + throw err } } // Delete the _rev and _id keys. No need for them in the resource object. They will break things. - if ("_rev" in data) { - delete resReq.data._rev; + if ('_rev' in data) { + delete resReq.data._rev } - var resource = await put(resReq); - return { link, resource }; + var resource = await put(resReq) + return { link, resource } } - function _watch({ headers, path, callback, payload }) { + function _watch ({ headers, path, callback, payload }) { if (SOCKET) { return SOCKET.watch( { path, - headers, + headers }, - async function handleWatchResponse(response) { + async function handleWatchResponse (response) { if (payload && payload.tree) { // Filter the change body based on the given tree - info("BODY BEFORE", path, payload.tree); - info(_.cloneDeep(response.change.body)); + info('BODY BEFORE', path, payload.tree) + info(_.cloneDeep(response.change.body)) response.change.body = await _recursiveFilterChange( DOMAIN + path, payload.tree, - response.change.body, - ); - info("BODY AFTER"); - info(response.change.body); + response.change.body + ) + info('BODY AFTER') + info(response.change.body) } - var watchPayload = _.cloneDeep(payload) || {}; - watchPayload.response = response; + var watchPayload = _.cloneDeep(payload) || {} + watchPayload.response = response watchPayload.request = { url: DOMAIN + path, headers, - method: response.change.type, - }; + method: response.change.type + } try { if (CACHE) { - watchPayload = await CACHE.handleWatchChange(watchPayload); + watchPayload = await CACHE.handleWatchChange(watchPayload) } } catch (err) { - error(err); - throw err; + error(err) + throw err } if (callback) { - await callback(watchPayload); + await callback(watchPayload) } - return; - }, - ); + return + } + ) } else { - throw new Error("websocket is required to watch resource"); + throw new Error('websocket is required to watch resource') } } // Construct the request object and catch any 401s (expired token) - async function _buildRequest({ method, url, path, headers, data, type }) { + async function _buildRequest ({ method, url, path, headers, data, type }) { if (!path && !url) { - throw new Error("Either path or url must be specified."); + throw new Error('Either path or url must be specified.') } if (url) { if (/^\//.test(url)) { - url = domain + url; + url = domain + url } else if (url.indexOf(domain) !== 0) { - throw new Error(`'url' key must begin with the domain used to connect`); + throw new Error(`'url' key must begin with the domain used to connect`) } } - method = method.toLowerCase(); + method = method.toLowerCase() let req = { method, url: url || DOMAIN + path, - headers: { authorization: "Bearer " + TOKEN }, - }; + headers: { authorization: 'Bearer ' + TOKEN } + } if (/\/$/.test(req.url)) { - req.url = req.url.slice(0, req.url.length - 1); + req.url = req.url.slice(0, req.url.length - 1) } //handle headers Object.keys(headers || {}).forEach(header => { - req.headers[header.toLowerCase()] = headers[header]; - }); + req.headers[header.toLowerCase()] = headers[header] + }) - if (method === "put" || method === "post") { - req.headers["content-type"] = - req.headers["content-type"] || type || data._type; - req.data = data; + if (method === 'put' || method === 'post') { + req.headers['content-type'] = + req.headers['content-type'] || type || data._type + req.data = data } - if (method === "delete") { - req.headers["content-type"] = req.headers["content-type"] || type; + if (method === 'delete') { + req.headers['content-type'] = req.headers['content-type'] || type } - req.requestStack = new Error().stack; - return req; + req.requestStack = new Error().stack + return req } - async function _sendRequest(req) { + async function _sendRequest (req) { try { - return REQUEST(req); + return REQUEST(req) } catch (err) { if (err && err.response.status === 401) { //token has expired - await reconnect(); - return REQUEST(req); + await reconnect() + return REQUEST(req) } - throw err; + throw err } } - async function _treeWalk(url, tree, data, obj, beforeCb, afterCb) { - var bef = beforeCb ? await beforeCb(url, tree, data, obj) : { data, obj }; - data = bef.data; - obj = bef.obj; - - info("mapping over data", url); - return Promise.map(Object.keys(data || {}), async function(key) { - info("key is", key); - info(typeof data[key], tree); - if (typeof data[key] === "object") { - var nextTree; + async function _treeWalk (url, tree, data, obj, beforeCb, afterCb) { + var bef = beforeCb ? await beforeCb(url, tree, data, obj) : { data, obj } + data = bef.data + obj = bef.obj + + info('mapping over data', url) + return Promise.map(Object.keys(data || {}), async function (key) { + info('key is', key) + info(typeof data[key], tree) + if (typeof data[key] === 'object') { + var nextTree if (tree[key]) { - nextTree = tree[key]; - } else if (tree["*"]) { - nextTree = tree["*"]; + nextTree = tree[key] + } else if (tree['*']) { + nextTree = tree['*'] //Leave alone data for any keys that are not present in tree. Do not //pursue these keys any further. } else { - return; + return } - info("next data", data[key]); + info('next data', data[key]) var res = await _treeWalk( - url + "/" + key, + url + '/' + key, nextTree, data[key], obj, beforeCb, - afterCb, - ); - info(key, res); - data[key] = res.data; - obj = res.obj; + afterCb + ) + info(key, res) + data[key] = res.data + obj = res.obj } - return; - }).then(async function() { - return afterCb ? await afterCb(url, tree, data, obj) : { data, obj }; - }); + return + }).then(async function () { + return afterCb ? await afterCb(url, tree, data, obj) : { data, obj } + }) } // walk down the tree to - function _recursiveFilterChange(url, tree, data) { + function _recursiveFilterChange (url, tree, data) { //Filter at resource breaks - return Promise.map(Object.keys(data || {}), async function(key) { - if (data[key] && typeof data[key] === "object") { + return Promise.map(Object.keys(data || {}), async function (key) { + if (data[key] && typeof data[key] === 'object') { if (tree[key]) { var res = await _recursiveFilterChange( - url + "/" + key, + url + '/' + key, tree[key], - data[key], - ); - return (data[key] = res.data); - } else if (tree["*"]) { + data[key] + ) + return (data[key] = res.data) + } else if (tree['*']) { var res = await _recursiveFilterChange( - url + "/" + key, - tree["*"], - data[key], - ); - return (data[key] = res.data); + url + '/' + key, + tree['*'], + data[key] + ) + return (data[key] = res.data) } else if (data[key]._id) { - let rv = data[key]._rev ? data[key]._rev : false; - data[key] = { _id: data[key]._id }; + let rv = data[key]._rev ? data[key]._rev : false + data[key] = { _id: data[key]._id } if (rv) { - data[key]._rev = data[key]._rev; + data[key]._rev = data[key]._rev } - return; + return } } else { - return; + return } }).then(() => { - return { data }; - }); + return { data } + }) } //TODO: patched up uncaughtrejection warning on _sendRequest with try/catch //but need to reevaluate better code structuring - async function get({ url, path, headers, watch, tree }) { - let req = await _buildRequest({ method: "get", url, path, headers }); + async function get ({ url, path, headers, watch, tree }) { + let req = await _buildRequest({ method: 'get', url, path, headers }) // If a tree is supplied, recursively GET data according to the data tree // The tree must be rooted at /bookmarks. - let watchResponse; + let watchResponse // TODO: shouldn't request twice for normal tree get... - var response = await _sendRequest(req); + var response = await _sendRequest(req) - let subTree; + let subTree // Use the tree to construct the subTree to be potentially used in both the // watch as well as the recursiveGet if (tree) { let pieces = urlLib .parse(req.url) - .path.replace(/^\//, "") - .split("/"); - let treePath = _convertSetupTreePath(pieces, tree); - info(tree, treePath); + .path.replace(/^\//, '') + .split('/') + let treePath = _convertSetupTreePath(pieces, tree) + info(tree, treePath) if (!pointer.has(tree, treePath)) { - throw new Error("The path does not exist on the given tree."); + throw new Error('The path does not exist on the given tree.') } - subTree = pointer.get(tree, treePath); + subTree = pointer.get(tree, treePath) } // Watch a resource; if a tree is also supplied, attach this to the payload // so that change notifications can be filtered through the tree if (watch) { - path = path || urlLib.parse(url).path; - req.headers["x-oada-rev"] = response.data._rev; + path = path || urlLib.parse(url).path + req.headers['x-oada-rev'] = response.data._rev - info("Setting watch. Sending rev:", response.data._rev); + info('Setting watch. Sending rev:', response.data._rev) if (tree) { - if (!watch.payload) watch.payload = {}; - watch.payload.tree = subTree; + if (!watch.payload) watch.payload = {} + watch.payload.tree = subTree } // Deprecation warnings if (watch.function || watch.func) { - error("Deprecation warning: use callback to pass a watch handler."); + error('Deprecation warning: use callback to pass a watch handler.') } - let callback = watch.callback || watch.function || watch.func; + let callback = watch.callback || watch.function || watch.func if (!callback) { // Just a warning message. It doesn't throw but print an error message. - error("Warning: no watch handler was provided."); + error('Warning: no watch handler was provided.') } watchResponse = await _watch({ @@ -430,262 +426,273 @@ var connect = async function connect({ path, callback, payload: watch.payload - }); - info("WATCH RESPONSE", watchResponse); + }) + info('WATCH RESPONSE', watchResponse) } // Perform recursive GET in response to the tree if (tree) { try { if (watch && watchResponse.resource) { - var dataOut = await _recursiveGet(req.url, subTree, watchResponse.resource, true); - error("tree getting", watch, watchResponse.resource); var dataOut = await _recursiveGet( req.url, subTree, watchResponse.resource, - true, - ); + true + ) + error('tree getting', watch, watchResponse.resource) + var dataOut = await _recursiveGet( + req.url, + subTree, + watchResponse.resource, + true + ) } else { - var dataOut = await _recursiveGet(req.url, subTree, response.data, true); + var dataOut = await _recursiveGet( + req.url, + subTree, + response.data, + true + ) } - response.data = dataOut.data; - response.cached = dataOut.cached; - } catch (err) { // catch 404s because ... ? + response.data = dataOut.data + response.cached = dataOut.cached + } catch (err) { + // catch 404s because ... ? if (err.response && err.response.status === 404) { - error("Received 404"); - } else throw err; + error('Received 404') + } else throw err } } - return response; + return response } //get - async function _recursiveGet(url, tree, data, cached) { + async function _recursiveGet (url, tree, data, cached) { // Perform a GET if we have reached the next resource break. if (tree._type) { // its a resource let response = await get({ url, - headers: "_rev" in data ? { "x-oada-rev": data._rev } : {}, - }); - data = response.data; - cached = response.cached ? response.cached && cached : false; + headers: '_rev' in data ? { 'x-oada-rev': data._rev } : {} + }) + data = response.data + cached = response.cached ? response.cached && cached : false } - return Promise.map(Object.keys(data || {}), async function(key) { - if (typeof data[key] === "object") { + return Promise.map(Object.keys(data || {}), async function (key) { + if (typeof data[key] === 'object') { if (tree[key]) { let res = await _recursiveGet( - url + "/" + key, + url + '/' + key, tree[key], data[key], - cached, - ); - cached = cached && res.cached; - return (data[key] = res.data); - } else if (tree["*"]) { + cached + ) + cached = cached && res.cached + return (data[key] = res.data) + } else if (tree['*']) { let res = await _recursiveGet( - url + "/" + key, - tree["*"], + url + '/' + key, + tree['*'], data[key], - cached, - ); - cached = cached && res.cached; - return (data[key] = res.data); + cached + ) + cached = cached && res.cached + return (data[key] = res.data) } else { - return; + return } //data[key] is already stored in the data object } else { - return; + return } }).then(() => { - return { data, cached }; - }); + return { data, cached } + }) } // Identify the stored resources vs those that need to be setup. - function _findDeepestResources(pieces, tree, storedTree) { - let stored = 0; - let setup; - var _rev; + function _findDeepestResources (pieces, tree, storedTree) { + let stored = 0 + let setup + var _rev // Walk down the url in reverse order return Promise.mapSeries(pieces, (piece, i) => { - let z = pieces.length - 1 - i; //use z to create paths in reverse order - let urlPath = "/" + pieces.slice(0, z + 1).join("/"); - let treePath = _convertSetupTreePath(pieces.slice(0, z + 1), tree); + let z = pieces.length - 1 - i //use z to create paths in reverse order + let urlPath = '/' + pieces.slice(0, z + 1).join('/') + let treePath = _convertSetupTreePath(pieces.slice(0, z + 1), tree) // Check that its in the stored tree then look for deepest _resource_. // If successful, break from the loop by throwing - if (pointer.has(tree, treePath + "/_type")) { - setup = setup || z; + if (pointer.has(tree, treePath + '/_type')) { + setup = setup || z if (pointer.has(storedTree, urlPath)) { - stored = _.clone(z); - throw new Error("stored"); + stored = _.clone(z) + throw new Error('stored') } return get({ - path: urlPath, + path: urlPath }) .then(response => { //TODO: Detect whether the returned data matches the given tree - pointer.set(storedTree, urlPath, {}); - stored = _.clone(z); - _rev = response.headers["x-oada-rev"]; - throw new Error("stored"); + pointer.set(storedTree, urlPath, {}) + stored = _.clone(z) + _rev = response.headers['x-oada-rev'] + throw new Error('stored') }) .catch(err => { if (/^stored/.test(err.message)) { - throw err; + throw err } - return; - }); + return + }) } else { - return; + return } }) .catch(err => { // Throwing with a number error only should occur on success. if (/^stored/.test(err.message)) { - return { stored, setup }; + return { stored, setup } } }) .then(() => { return { stored: stored, setup: setup || 0, - _rev, - }; - }); + _rev + } + }) } // Loop over the keys of the path and determine whether the object at that level // contains a * key. The path must be updated along the way, replacing *s as // necessary. - function _convertSetupTreePath(pathPieces, tree) { - let newPieces = _.clone(pathPieces); + function _convertSetupTreePath (pathPieces, tree) { + let newPieces = _.clone(pathPieces) newPieces = pathPieces.map((piece, i) => { - if (pointer.has(tree, "/" + newPieces.slice(0, i).join("/") + "/*")) { - newPieces[i] = "*"; - return "*"; + if (pointer.has(tree, '/' + newPieces.slice(0, i).join('/') + '/*')) { + newPieces[i] = '*' + return '*' } else { - return piece; + return piece } - }); - return "/" + newPieces.join("/"); + }) + return '/' + newPieces.join('/') } // Walk down the given path using the tree as a guide to create the necessary // resource breaks along the way. - async function _ensureTree({ url, tree, data }) { - let path = urlLib.parse(url).path.replace(/^\//, ""); - let pieces = path.replace(/\/$/, "").split("/"); // replace trailing slashes + async function _ensureTree ({ url, tree, data }) { + let path = urlLib.parse(url).path.replace(/^\//, '') + let pieces = path.replace(/\/$/, '').split('/') // replace trailing slashes if (data._id) { - let firstPath = _convertSetupTreePath(pieces, tree); - pointer.set(tree, firstPath + "/_id", data._id); + let firstPath = _convertSetupTreePath(pieces, tree) + pointer.set(tree, firstPath + '/_id', data._id) } // if (pointer.has(tree, treePath)) pointer.set(tree, treePath, _.merge(pointer.get(tree, treePath),data)) - let storedTree = {}; - var responses = []; + let storedTree = {} + var responses = [] // Find the deepest part of the path that exists. Once found, work back down. - var ret = await _findDeepestResources(pieces, tree, storedTree); + var ret = await _findDeepestResources(pieces, tree, storedTree) // Create all the resources on the way down. ret.stored is an index. Slice // takes the length to slice, so no need to subtract 1. - var parentRev = ret._rev; + var parentRev = ret._rev await Promise.mapSeries( pieces.slice(0, pieces.length - ret.stored), - async function(piece, j) { - let i = ret.stored + 1 + j; - let urlPath = "/" + pieces.slice(0, i + 1).join("/"); - let treePath = _convertSetupTreePath(pieces.slice(0, i + 1), tree); - if (pointer.has(tree, treePath + "/_type") && i <= ret.setup) { + async function (piece, j) { + let i = ret.stored + 1 + j + let urlPath = '/' + pieces.slice(0, i + 1).join('/') + let treePath = _convertSetupTreePath(pieces.slice(0, i + 1), tree) + if (pointer.has(tree, treePath + '/_type') && i <= ret.setup) { // its a resource - var content = await _replaceLinks(pointer.get(tree, treePath)); + var content = await _replaceLinks(pointer.get(tree, treePath)) var resp = await _makeResourceAndLink({ path: urlPath, data: _.cloneDeep(content), - headers: { "if-match": parentRev }, - }); - parentRev = resp.resource.headers["x-oada-rev"]; - pointer.set(storedTree, urlPath, content); - resp.path = urlPath; - responses.push(resp); + headers: { 'if-match': parentRev } + }) + parentRev = resp.resource.headers['x-oada-rev'] + pointer.set(storedTree, urlPath, content) + resp.path = urlPath + responses.push(resp) } - }, - ); - return responses; + } + ) + return responses } - function _configureCache({ name, req, expires }) { - let res = setupCache({ name, req, expires, dbprefix }); - REQUEST = res.api; - CACHE = res; - return; + function _configureCache ({ name, req, expires }) { + let res = setupCache({ name, req, expires, dbprefix }) + REQUEST = res.api + CACHE = res + return } - function post({ url, path, data, type, headers, tree }) { - url = url || DOMAIN + path; - url = url[url.length - 1] === "/" ? url + uuid() : url + "/" + uuid(); + function post ({ url, path, data, type, headers, tree }) { + url = url || DOMAIN + path + url = url[url.length - 1] === '/' ? url + uuid() : url + '/' + uuid() return put({ url, data, type, headers, - tree, - }); + tree + }) } - async function _recursiveDelete(url, tree, data) { + async function _recursiveDelete (url, tree, data) { // Perform a GET if we have reached the next resource break. if (tree._type) { // its a resource try { var got = await get({ - url, - }); - data = got.data; + url + }) + data = got.data } catch (err) { if (err.status === 404) { - data = {}; - // return; + data = {} + // return; } } } - return Promise.map(Object.keys(data || {}), async function(key) { - if (typeof data[key] === "object") { + return Promise.map(Object.keys(data || {}), async function (key) { + if (typeof data[key] === 'object') { if (tree[key]) { var res = await _recursiveDelete( - url + "/" + key, + url + '/' + key, tree[key], - data[key], - ); - return (data[key] = res.data); - } else if (tree["*"]) { + data[key] + ) + return (data[key] = res.data) + } else if (tree['*']) { var res = await _recursiveDelete( - url + "/" + key, - tree["*"], - data[key], - ); + url + '/' + key, + tree['*'], + data[key] + ) info('url', url) info('recursiveDelete. returning', res.data) - return (data[key] = res.data); + return (data[key] = res.data) } else { - return; + return } //data[key] is already stored in the data object } else { - return; + return } - }).then(async function() { - var link; + }).then(async function () { + var link if (tree._type) { try { // Delete the resource if (data._id) { try { await del({ - path: "/" + data._id, - headers: { "content-type": tree._type }, - }); + path: '/' + data._id, + headers: { 'content-type': tree._type } + }) } catch (erro) { - error("aaaaaaaa", "/" + data._id, url, erro); - throw erro; + error('aaaaaaaa', '/' + data._id, url, erro) + throw erro } } @@ -693,153 +700,150 @@ var connect = async function connect({ link = await del({ url, headers: { - "content-type": tree._type, - }, - }); + 'content-type': tree._type + } + }) } catch (err) { if (err.response && err.response.status === 404) { - data = {}; + data = {} return { link: link || {}, - data: data || {}, - }; + data: data || {} + } } - throw err; + throw err } } else { // Delete the lookup for non resources if (CACHE) { - await CACHE.removeLookup({ url }); + await CACHE.removeLookup({ url }) } } // link and data can be undefined if the current object is // not a resource (deletes won't need to happen) return { link: link || {}, - data: data || {}, - }; + data: data || {} + } }) } - async function del({ url, path, type, headers, tree, unwatch }) { + function unwatch (handler) { + return SOCKET.unwatch(handler) + } + + async function del ({ url, path, type, headers, tree }) { let req = await _buildRequest({ - method: "delete", + method: 'delete', url, path, type, - headers, - }); - if (unwatch) { - path = path || urlLib.parse(url).path; - return SOCKET.unwatch({ - path, - headers: req.headers, - }); - } + headers + }) if (tree) { var pieces = urlLib .parse(req.url) - .path.replace(/^\//, "") - .split("/"); - let treePath = _convertSetupTreePath(pieces, tree); + .path.replace(/^\//, '') + .split('/') + let treePath = _convertSetupTreePath(pieces, tree) if (!pointer.has(tree, treePath)) { - throw new Error("The path does not exist on the given tree."); + throw new Error('The path does not exist on the given tree.') } //return get({url: req.url}) - var subTree = pointer.get(tree, treePath); - var result = await _recursiveDelete(req.url, subTree, {}, true); - return result.link; + var subTree = pointer.get(tree, treePath) + var result = await _recursiveDelete(req.url, subTree, {}, true) + return result.link //var treePath = _convertSetupTreePath(pieces, tree) + "/_type"; //if (!req.headers["content-type"] && pointer.has(tree, treePath)) // req.headers["content-type"] = _.clone(pointer.get(tree, treePath)); } - if (!req.headers["content-type"]) { - throw new Error(`content-type header must be specified.`); + if (!req.headers['content-type']) { + throw new Error(`content-type header must be specified.`) } - return _sendRequest(req); + return _sendRequest(req) } // Ensure all resources down to the deepest resource are created before // performing a PUT. - async function put({ url, path, data, type, headers, tree }) { + async function put ({ url, path, data, type, headers, tree }) { let req = await _buildRequest({ - method: "put", + method: 'put', url, path, data, type, - headers, - }); + headers + }) // Ensure parent resources are created if (tree) { var responses = await _ensureTree({ url: req.url, tree: _.cloneDeep(tree), - data, - }); + data + }) var pieces = responses.length > 0 - ? responses[responses.length - 1].path.replace(/^\//, "").split("/") + ? responses[responses.length - 1].path.replace(/^\//, '').split('/') : urlLib .parse(req.url) - .path.replace(/^\//, "") - .split("/"); + .path.replace(/^\//, '') + .split('/') - var treePath = _convertSetupTreePath(pieces, tree) + "/_type"; - if (!req.headers["content-type"] && pointer.has(tree, treePath)) { - req.headers["content-type"] = _.clone(pointer.get(tree, treePath)); + var treePath = _convertSetupTreePath(pieces, tree) + '/_type' + if (!req.headers['content-type'] && pointer.has(tree, treePath)) { + req.headers['content-type'] = _.clone(pointer.get(tree, treePath)) } } - if (!req.headers["content-type"]) { - throw new Error(`content-type header must be specified.`); - info('PUT - tree ensured. Executing PUT', req); + if (!req.headers['content-type']) { + throw new Error(`content-type header must be specified.`) + info('PUT - tree ensured. Executing PUT', req) } return _sendRequest(req).then(result => { - return result; - }); + return result + }) } - async function resetCache(name, expires) { + async function resetCache (name, expires) { if (!CACHE) { - return; + return } - await CACHE.resetCache(); + await CACHE.resetCache() } - async function disconnect() { + async function disconnect () { if (CACHE) { - await CACHE.db.close(); + await CACHE.db.close() } //if (CACHE) await CACHE.db.destroy(); if (SOCKET) { - SOCKET.close(); + SOCKET.close() } //if (_token.isSet()) { - _token.cleanUp(); + _token.cleanUp() //} } - async function reconnect() { + async function reconnect () { // get a new token - TOKEN = await _token.renew(); + TOKEN = await _token.renew() } // get a token - TOKEN = await _token.get(); + TOKEN = await _token.get() if (!TOKEN) { - throw new Error("Unable to get token"); + throw new Error('Unable to get token') } // Setup websockets if (websocket !== false) { - var socketApi = await ws(domain); - NOCACHEREQUEST = socketApi.http; - REQUEST = socketApi.http; - SOCKET = await socketApi; + var socketApi = await ws(domain) + NOCACHEREQUEST = socketApi.http + REQUEST = socketApi.http + SOCKET = await socketApi } //Setup the cache @@ -847,15 +851,15 @@ var connect = async function connect({ await _configureCache({ name: NAME || uuid(), req: REQUEST, - expires: EXPIRES, - }); + expires: EXPIRES + }) } - function _getMemoryCache() { + function _getMemoryCache () { if (CACHE) { - return CACHE._getMemoryCache(); + return CACHE._getMemoryCache() } else { - return {}; + return {} } } @@ -864,43 +868,46 @@ var connect = async function connect({ cache: CACHE ? CACHE : false, websocket: SOCKET ? SOCKET : false, get, + watch: ({ url, path, headers, tree, ...watch }) => + get({ url, path, headers, tree, watch }), + unwatch, put, post, delete: del, resetCache, disconnect, reconnect, - _getMemoryCache, - }; -}; + _getMemoryCache + } +} -const resetDomainCache = async function(domain) { - trace('resetDomainCache: setting up cache'); +const resetDomainCache = async function (domain) { + trace('resetDomainCache: setting up cache') const cache = setupCache({ name: domainToCacheName(domain), dbprefix }) - trace('resetDomainCache: cache is setup, awaiting reset'); - await cache.resetCache(); - trace('resetDomainCache: cache reset done, awaiting domain token reset'); - await clearDomainToken(domain); - trace('resetDomainCache: DONE!'); + trace('resetDomainCache: cache is setup, awaiting reset') + await cache.resetCache() + trace('resetDomainCache: cache reset done, awaiting domain token reset') + await clearDomainToken(domain) + trace('resetDomainCache: DONE!') } -const clearDomainToken = async function(domain) { - trace('clearDomainToken: creating new Token lib'); - const token = new _TOKEN({ domain, dbprefix }); - trace('clearDomainToken: created new Token lib, awaiting cleanUp()'); - await token.cleanUp(); - trace('clearDomainToken: DONE!'); +const clearDomainToken = async function (domain) { + trace('clearDomainToken: creating new Token lib') + const token = new _TOKEN({ domain, dbprefix }) + trace('clearDomainToken: created new Token lib, awaiting cleanUp()') + await token.cleanUp() + trace('clearDomainToken: DONE!') } -const getDomainToken = async function(domain) { - trace('getDomainToken: creating token lib'); - const token = new _TOKEN({domain,dbprefix}); - trace('getDomainToken: token lib created, checking for token in DB'); - return await token.checkTokenDB(); // returns the token string +const getDomainToken = async function (domain) { + trace('getDomainToken: creating token lib') + const token = new _TOKEN({ domain, dbprefix }) + trace('getDomainToken: token lib created, checking for token in DB') + return await token.checkTokenDB() // returns the token string } -const haveDomainToken = async function(domain) { - return !!(await getDomainToken(domain)); +const haveDomainToken = async function (domain) { + return !!(await getDomainToken(domain)) } module.exports = { @@ -909,5 +916,5 @@ module.exports = { clearDomainToken, haveDomainToken, getDomainToken, - setDbPrefix, -}; + setDbPrefix +} diff --git a/src/websocket.js b/src/websocket.js index 5258917..105dbac 100644 --- a/src/websocket.js +++ b/src/websocket.js @@ -1,96 +1,114 @@ -const Promise = require("bluebird"); -const urlLib = require("url"); -const uuid = require("uuid/v4"); -const WebSocket = require("isomorphic-ws"); -Promise.config({ warnings: false }); +const Promise = require('bluebird') +const urlLib = require('url') +const uuid = require('uuid/v4') +const WebSocket = require('isomorphic-ws') +Promise.config({ warnings: false }) -function websocket(url) { +function websocket (url) { //Create the message queue - var messages = []; + var messages = [] //Create the socket - url = url.replace("https://", "wss://").replace("http://", "ws://"); - var socket = new WebSocket(url); - var connected = false; - var httpCallbacks = {}; - var watchCallbacks = {}; + url = url.replace('https://', 'wss://').replace('http://', 'ws://') + var socket = new WebSocket(url) + var connected = false + var httpCallbacks = {} + var watchCallbacks = {} - function sendMessages() { + function sendMessages () { if (!connected) { - return; + return } messages.forEach(message => { - socket.send(JSON.stringify(message)); - }); - messages = []; + socket.send(JSON.stringify(message)) + }) + messages = [] } return new Promise((resolve, reject) => { - socket.onopen = function(event) { - connected = true; - sendMessages(); - resolve(socket); - }; + socket.onopen = function (event) { + connected = true + sendMessages() + resolve(socket) + } + + socket.oncloconsose = function (event) {} + socket.onmessage = function (event) { + const response = JSON.parse(event.data) - socket.oncloconsose = function(event) {}; - socket.onmessage = function(event) { - var response = JSON.parse(event.data); - //Look for id in httpCallbacks - if (response.requestId) { - if (httpCallbacks[response.requestId]) { - //Resolve Promise - if (response.status >= 200 && response.status < 300) { - httpCallbacks[response.requestId].resolve(response); - } else { - //Create error like axios - let err = new Error( - "Request failed with status code " + response.status - ); - err.request = httpCallbacks[response.requestId].request; - err.response = { - status: response.status, - statusText: response.statusText, - headers: response.headers, - data: response.data, - }; - err.originalStack = httpCallbacks[response.requestId].request.requestStack; + if (Array.isArray(response.requestId)) { + // Handle muxed repsonses + Promise.map(response.requestId, (requestId, i) => { + // Create separate responses + return { + ...response, + requestId, + path_leftover: response.path_leftover[i] + } + }).map(handleResponse) + } else { + handleResponse(response) + } - httpCallbacks[response.requestId].reject(err); - } - delete httpCallbacks[response.requestId]; - } else if (watchCallbacks[response.requestId]) { - if (watchCallbacks[response.requestId].resolve) { - if (response.status === "success") { - //Successfully setup websocket, resolve promise - watchCallbacks[response.requestId].resolve(response); + function handleResponse (response) { + //Look for id in httpCallbacks + if (response.requestId) { + if (httpCallbacks[response.requestId]) { + //Resolve Promise + if (response.status >= 200 && response.status < 300) { + httpCallbacks[response.requestId].resolve(response) } else { - //error(watchCallbacks[response.requestId].request, response); + //Create error like axios let err = new Error( - "Request failed with status code " + response.status - ); - err.response = response; - err.request = watchCallbacks[response.requestId].request; - watchCallbacks[response.requestId].reject(err); + 'Request failed with status code ' + response.status + ) + err.request = httpCallbacks[response.requestId].request + err.response = { + status: response.status, + statusText: response.statusText, + headers: response.headers, + data: response.data + } + err.originalStack = + httpCallbacks[response.requestId].request.requestStack + + httpCallbacks[response.requestId].reject(err) } - //Remove resolve and reject so we process change as a signal next time - delete watchCallbacks[response.requestId]["resolve"]; - delete watchCallbacks[response.requestId]["reject"]; - } else { - if (watchCallbacks[response.requestId].callback == null) { - throw new Error( - "The given watch function has an undefined callback:", - watchCallbacks[response.requestId] - ); + delete httpCallbacks[response.requestId] + } else if (watchCallbacks[response.requestId]) { + if (watchCallbacks[response.requestId].resolve) { + if (response.status === 200) { + //Successfully setup websocket, resolve promise + watchCallbacks[response.requestId].resolve(response) + } else { + //error(watchCallbacks[response.requestId].request, response); + let err = new Error( + 'Request failed with status code ' + response.status + ) + err.response = response + err.request = watchCallbacks[response.requestId].request + watchCallbacks[response.requestId].reject(err) + } + //Remove resolve and reject so we process change as a signal next time + delete watchCallbacks[response.requestId]['resolve'] + delete watchCallbacks[response.requestId]['reject'] + } else { + if (watchCallbacks[response.requestId].callback == null) { + throw new Error( + 'The given watch function has an undefined callback:', + watchCallbacks[response.requestId] + ) + } + watchCallbacks[response.requestId].callback(response) } - watchCallbacks[response.requestId].callback(response); } } } - }; + } }).then(() => { - function _http(request) { + function _http (request) { //Do a HTTP request return new Promise((resolve, reject) => { - let urlObj = urlLib.parse(request.url); + let urlObj = urlLib.parse(request.url) let message = { requestId: uuid(), method: request.method.toLowerCase(), @@ -98,82 +116,80 @@ function websocket(url) { data: request.data, headers: Object.entries(request.headers) .map(([key, value]) => { - return { [key.toLowerCase()]: value }; + return { [key.toLowerCase()]: value } }) .reduce((a, b) => { - return { ...a, ...b }; - }), - }; - messages.push(message); + return { ...a, ...b } + }) + } + messages.push(message) httpCallbacks[message.requestId] = { request: request, resolve: resolve, - reject: reject, - }; - sendMessages(); - }); + reject: reject + } + sendMessages() + }) } - function _unwatch(request, callback) { + function _unwatch (handler) { + let requestId + for (requestId in watchCallbacks) { + if (watchCallbacks[requestId] === handler) { + break + } + } //Watch for changes on requested resource and trigger provided signal return new Promise((resolve, reject) => { - let message = { - requestId: uuid(), - method: "unwatch", - path: request.path, - headers: Object.entries(request.headers) - .map(([key, value]) => { - return { [key.toLowerCase()]: value }; - }) - .reduce((a, b) => { - return { ...a, ...b }; - }), - }; - messages.push(message); - watchCallbacks[message.requestId] = { - request, + const message = { + requestId, + method: 'unwatch' + } + delete watchCallbacks[message.requestId] + + messages.push(message) + httpCallbacks[message.requestId] = { resolve, - reject, - callback, - }; - sendMessages(); - }); + reject + } + sendMessages() + }) } - function _watch(request, callback) { + function _watch (request, callback) { //Watch for changes on requested resource and trigger provided signal return new Promise((resolve, reject) => { let message = { requestId: uuid(), - method: "watch", + method: 'watch', path: request.path, headers: Object.entries(request.headers) .map(([key, value]) => { - return { [key.toLowerCase()]: value }; + return { [key.toLowerCase()]: value } }) .reduce((a, b) => { - return { ...a, ...b }; - }), - }; - messages.push(message); + return { ...a, ...b } + }) + } + messages.push(message) watchCallbacks[message.requestId] = { request, resolve, reject, - callback, - }; - sendMessages(); - }); + callback + } + sendMessages() + }) } - function _close() { + function _close () { //TODO reject all callbacks that have not resolved //Clear everything - messages = []; - httpCallbacks = {}; - watchCallbacks = {}; + messages = [] + httpCallbacks = {} + watchCallbacks = {} //Close socket - socket.close(); + socket.close() } return { @@ -181,9 +197,9 @@ function websocket(url) { http: _http, close: _close, watch: _watch, - unwatch: _unwatch, - }; - }); + unwatch: _unwatch + } + }) } -module.exports = websocket; +module.exports = websocket diff --git a/test/connections.test.js b/test/connections.test.js index 1a9516f..33f8602 100644 --- a/test/connections.test.js +++ b/test/connections.test.js @@ -1,238 +1,239 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const { token, domain } = require("./config.js"); -const oada = require("../src/index"); -const chai = require("chai"); -var chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised); -var expect = chai.expect; -oada.setDbPrefix("./test/test-data/"); +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const { token, domain } = require('./config.js') +const oada = require('../src/index') +const chai = require('chai') +var chaiAsPromised = require('chai-as-promised') +chai.use(chaiAsPromised) +var expect = chai.expect +oada.setDbPrefix('./test/test-data/') -let connections = new Array(4); -let connectTime = 30 * 1000; // seconds to click through oauth +let connections = new Array(4) +let connectTime = 30 * 1000 // seconds to click through oauth -describe("~~~~~~ CONNECTIONS~~~~~~~", function() { - this.timeout(connectTime); +describe('~~~~~~ CONNECTIONS~~~~~~~', function () { + this.timeout(connectTime) + + it('Should connect with metadata. Browser popup must be used to login within 30s.', async function () { + this.timeout(connectTime) - it("Should connect with metadata. Browser popup must be used to login within 30s.", async function() { - this.timeout(connectTime); var result = await oada.connect({ - domain, - options: { - redirect: "http://localhost:8000/oauth2/redirect.html", - metadata: - "eyJqa3UiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbS9jZXJ0cyIsImtpZCI6ImtqY1NjamMzMmR3SlhYTEpEczNyMTI0c2ExIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJyZWRpcmVjdF91cmlzIjpbImh0dHA6Ly92aXAzLmVjbi5wdXJkdWUuZWR1OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiLCJodHRwOi8vbG9jYWxob3N0OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2QiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1iZWFyZXIiLCJncmFudF90eXBlcyI6WyJpbXBsaWNpdCJdLCJyZXNwb25zZV90eXBlcyI6WyJ0b2tlbiIsImlkX3Rva2VuIiwiaWRfdG9rZW4gdG9rZW4iXSwiY2xpZW50X25hbWUiOiJPcGVuQVRLIiwiY2xpZW50X3VyaSI6Imh0dHBzOi8vdmlwMy5lY24ucHVyZHVlLmVkdSIsImNvbnRhY3RzIjpbIlNhbSBOb2VsIDxzYW5vZWxAcHVyZHVlLmVkdT4iXSwic29mdHdhcmVfaWQiOiIxZjc4NDc3Zi0zNTQxLTQxM2ItOTdiNi04NjQ0YjRhZjViYjgiLCJyZWdpc3RyYXRpb25fcHJvdmlkZXIiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbSIsImlhdCI6MTUxMjAwNjc2MX0.AJSjNlWX8UKfVh-h1ebCe0MEGqKzArNJ6x0nmta0oFMcWMyR6Cn2saR-oHvU8WrtUMEr-w020mAjvhfYav4EdT3GOGtaFgnbVkIs73iIMtr8Z-Y6mDEzqRzNzVRMLghj7CyWRCNJEk0jwWjOuC8FH4UsfHmtw3ouMFomjwsNLY0", - scope: "oada.yield:all" - }, - cache: false - }) - expect(result).to.include.keys([ - "token", - "cache", - "websocket", - "disconnect", - "reconnect", - "get", - "put", - "post", - "delete", - "resetCache", -// "_getMemoryCache", - ]); - expect(result.cache).to.equal(false); - expect(result.websocket).to.be.a("object"); - expect(result.get).to.be.a("function"); - expect(result.put).to.be.a("function"); - expect(result.post).to.be.a("function"); - expect(result.resetCache).to.be.a("function"); - expect(result.disconnect).to.be.a("function"); - }); + domain, + options: { + redirect: 'http://localhost:8000/oauth2/redirect.html', + metadata: + 'eyJqa3UiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbS9jZXJ0cyIsImtpZCI6ImtqY1NjamMzMmR3SlhYTEpEczNyMTI0c2ExIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJyZWRpcmVjdF91cmlzIjpbImh0dHA6Ly92aXAzLmVjbi5wdXJkdWUuZWR1OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiLCJodHRwOi8vbG9jYWxob3N0OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2QiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1iZWFyZXIiLCJncmFudF90eXBlcyI6WyJpbXBsaWNpdCJdLCJyZXNwb25zZV90eXBlcyI6WyJ0b2tlbiIsImlkX3Rva2VuIiwiaWRfdG9rZW4gdG9rZW4iXSwiY2xpZW50X25hbWUiOiJPcGVuQVRLIiwiY2xpZW50X3VyaSI6Imh0dHBzOi8vdmlwMy5lY24ucHVyZHVlLmVkdSIsImNvbnRhY3RzIjpbIlNhbSBOb2VsIDxzYW5vZWxAcHVyZHVlLmVkdT4iXSwic29mdHdhcmVfaWQiOiIxZjc4NDc3Zi0zNTQxLTQxM2ItOTdiNi04NjQ0YjRhZjViYjgiLCJyZWdpc3RyYXRpb25fcHJvdmlkZXIiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbSIsImlhdCI6MTUxMjAwNjc2MX0.AJSjNlWX8UKfVh-h1ebCe0MEGqKzArNJ6x0nmta0oFMcWMyR6Cn2saR-oHvU8WrtUMEr-w020mAjvhfYav4EdT3GOGtaFgnbVkIs73iIMtr8Z-Y6mDEzqRzNzVRMLghj7CyWRCNJEk0jwWjOuC8FH4UsfHmtw3ouMFomjwsNLY0', + scope: 'oada.yield:all' + }, + cache: false + }) + expect(result).to.include.keys([ + 'token', + 'cache', + 'websocket', + 'disconnect', + 'reconnect', + 'get', + 'put', + 'post', + 'delete', + 'resetCache' + // "_getMemoryCache", + ]) + expect(result.cache).to.equal(false) + expect(result.websocket).to.be.a('object') + expect(result.get).to.be.a('function') + expect(result.put).to.be.a('function') + expect(result.post).to.be.a('function') + expect(result.resetCache).to.be.a('function') + expect(result.disconnect).to.be.a('function') + }) - it("Should make a connection with websocket and cache", async function() { - this.timeout(connectTime); + it('Should make a connection with websocket and cache', async function () { + this.timeout(connectTime) var result = await oada.connect({ domain, - token, + token }) - connections[0] = result; + connections[0] = result expect(result).to.include.keys([ - "token", - "disconnect", - "reconnect", - "get", - "put", - "post", - "delete", - "resetCache", - "cache", - "websocket", - "_getMemoryCache", - ]); - expect(result.cache).to.be.a("object"); - expect(result.websocket).to.be.a("object"); - expect(result.get).to.be.a("function"); - expect(result.put).to.be.a("function"); - expect(result.post).to.be.a("function"); - expect(result.resetCache).to.be.a("function"); - expect(result.disconnect).to.be.a("function"); - }); + 'token', + 'disconnect', + 'reconnect', + 'get', + 'put', + 'post', + 'delete', + 'resetCache', + 'cache', + 'websocket', + '_getMemoryCache' + ]) + expect(result.cache).to.be.a('object') + expect(result.websocket).to.be.a('object') + expect(result.get).to.be.a('function') + expect(result.put).to.be.a('function') + expect(result.post).to.be.a('function') + expect(result.resetCache).to.be.a('function') + expect(result.disconnect).to.be.a('function') + }) - it("Should make a connection with websocket off, cache on", async function() { - this.timeout(connectTime); + it('Should make a connection with websocket off, cache on', async function () { + this.timeout(connectTime) var result = await oada.connect({ - domain, - token, - websocket: false - }) - connections[1] = result; - expect(result).to.include.keys([ - "token", - "disconnect", - "reconnect", - "get", - "put", - "post", - "delete", - "resetCache", - "cache", - "websocket", - "_getMemoryCache", - ]); - expect(result.cache).to.be.a("object"); - expect(result.websocket).to.equal(false); - expect(result.get).to.be.a("function"); - expect(result.put).to.be.a("function"); - expect(result.post).to.be.a("function"); - expect(result.resetCache).to.be.a("function"); - expect(result.disconnect).to.be.a("function"); - }); + domain, + token, + websocket: false + }) + connections[1] = result + expect(result).to.include.keys([ + 'token', + 'disconnect', + 'reconnect', + 'get', + 'put', + 'post', + 'delete', + 'resetCache', + 'cache', + 'websocket', + '_getMemoryCache' + ]) + expect(result.cache).to.be.a('object') + expect(result.websocket).to.equal(false) + expect(result.get).to.be.a('function') + expect(result.put).to.be.a('function') + expect(result.post).to.be.a('function') + expect(result.resetCache).to.be.a('function') + expect(result.disconnect).to.be.a('function') + }) - it("Should make a connection with websocket on, cache off", async function() { - this.timeout(connectTime); + it('Should make a connection with websocket on, cache off', async function () { + this.timeout(connectTime) var result = await oada.connect({ - domain, - token: "def", - cache: false - }) - connections[2] = result; - expect(result).to.include.keys([ - "token", - "disconnect", - "get", - "put", - "post", - "reconnect", - "delete", - "resetCache", - "cache", - "websocket", - "_getMemoryCache", - ]); - expect(result.cache).to.equal(false); - expect(result.websocket).to.be.a("object"); - expect(result.get).to.be.a("function"); - expect(result.put).to.be.a("function"); - expect(result.post).to.be.a("function"); - expect(result.resetCache).to.be.a("function"); - expect(result.disconnect).to.be.a("function"); - }); - it("Should make a connection with websocket off, cache off", async function() { - this.timeout(connectTime); + domain, + token: 'def', + cache: false + }) + connections[2] = result + expect(result).to.include.keys([ + 'token', + 'disconnect', + 'get', + 'put', + 'post', + 'reconnect', + 'delete', + 'resetCache', + 'cache', + 'websocket', + '_getMemoryCache' + ]) + expect(result.cache).to.equal(false) + expect(result.websocket).to.be.a('object') + expect(result.get).to.be.a('function') + expect(result.put).to.be.a('function') + expect(result.post).to.be.a('function') + expect(result.resetCache).to.be.a('function') + expect(result.disconnect).to.be.a('function') + }) + it('Should make a connection with websocket off, cache off', async function () { + this.timeout(connectTime) var result = await oada.connect({ - domain, - token: "def", - websocket: false, - cache: false - }) - connections[3] = result; - expect(result).to.include.keys([ - "token", - "cache", - "websocket", - "_getMemoryCache", - "disconnect", - "get", - "reconnect", - "put", - "post", - "delete", - "resetCache" - ]); - expect(result.cache).to.equal(false); - expect(result.websocket).to.equal(false); - expect(result.get).to.be.a("function"); - expect(result.put).to.be.a("function"); - expect(result.post).to.be.a("function"); - expect(result.resetCache).to.be.a("function"); - expect(result.disconnect).to.be.a("function"); - }); + domain, + token: 'def', + websocket: false, + cache: false + }) + connections[3] = result + expect(result).to.include.keys([ + 'token', + 'cache', + 'websocket', + '_getMemoryCache', + 'disconnect', + 'get', + 'reconnect', + 'put', + 'post', + 'delete', + 'resetCache' + ]) + expect(result.cache).to.equal(false) + expect(result.websocket).to.equal(false) + expect(result.get).to.be.a('function') + expect(result.put).to.be.a('function') + expect(result.post).to.be.a('function') + expect(result.resetCache).to.be.a('function') + expect(result.disconnect).to.be.a('function') + }) - it("Should not make a connection without domain", async function() { - this.timeout(connectTime); + it('Should not make a connection without domain', async function () { + this.timeout(connectTime) expect( oada.connect({ - token: "def", + token: 'def', websocket: false, - cache: false, + cache: false }) - ).to.be.rejectedWith(Error, "domain undefined"); - }); + ).to.be.rejectedWith(Error, 'domain undefined') + }) - it("Should not make a connection without options and token", async function() { - this.timeout(connectTime); + it('Should not make a connection without options and token', async function () { + this.timeout(connectTime) await expect( oada.connect({ domain, websocket: false, - cache: false, + cache: false }) - ).to.be.rejectedWith(Error, "options and token undefined"); - }); + ).to.be.rejectedWith(Error, 'options and token undefined') + }) - it("Should not make a connection if token provided but not a string", async function() { - this.timeout(connectTime); + it('Should not make a connection if token provided but not a string', async function () { + this.timeout(connectTime) await expect( oada.connect({ domain, - token: { def: "def" }, + token: { def: 'def' }, websocket: false, - cache: false, + cache: false }) - ).to.be.rejectedWith(Error, "token must be a string"); - }); + ).to.be.rejectedWith(Error, 'token must be a string') + }) - it("Should not make a connection if websocket provided but not a boolean", async function() { - this.timeout(connectTime); + it('Should not make a connection if websocket provided but not a boolean', async function () { + this.timeout(connectTime) await expect( oada.connect({ domain, - token: "def", - websocket: "false", + token: 'def', + websocket: 'false' }) - ).to.be.rejectedWith(Error, "websocket must be boolean"); - }); + ).to.be.rejectedWith(Error, 'websocket must be boolean') + }) - it("Should not make a connection if cache provided but not a boolean", async function() { - this.timeout(connectTime); + it('Should not make a connection if cache provided but not a boolean', async function () { + this.timeout(connectTime) await expect( oada.connect({ domain, - token: "def", - cache: "false", + token: 'def', + cache: 'false' }) ).to.be.rejectedWith( Error, `cache must be either a boolean or an object with 'name' and/or 'expires' keys` - ); - }); + ) + }) /** * disconnections */ for (let i = 0; i < connections.length; i++) { - describe(`Disconnecting connection ${i + 1}`, function() { - it("Should disconnect", async () => { - connections[i].disconnect(); - }); - }); + describe(`Disconnecting connection ${i + 1}`, function () { + it('Should disconnect', async () => { + connections[i].disconnect() + }) + }) } //for -}); +}) diff --git a/test/delete.test.js b/test/delete.test.js index 8b3748c..76826a6 100644 --- a/test/delete.test.js +++ b/test/delete.test.js @@ -1,311 +1,308 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const Promise = require("bluebird"); -const _ = require("lodash"); -var chai = require("chai"); -var chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised); -var expect = chai.expect; -const { token, domain } = require("./config.js"); -const { tree, putResource, getConnections } = require("./utils.js"); +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const Promise = require('bluebird') +const chai = require('chai') +const chaiAsPromised = require('chai-as-promised') +chai.use(chaiAsPromised) +const { expect } = chai +const { token, domain } = require('./config.js') +const { tree, putResource, getConnections } = require('./utils.js') -var connections; -var resources = []; +const connections = getConnections({ + domain, + token +}) -describe(`------------DELETE-----------------`, async function() { - before(`Create connection types`, async function() { - connections = await getConnections({ - domain, - token, - }); - }); +describe(`------------DELETE-----------------`, function () { + for (const connection of connections) { + const { cache, websocket } = connection + describe(`Testing connection cache:${cache} websocket:${websocket}`, function () { + let conn + before('Wait on connection', async function () { + conn = await connection + }) + + beforeEach('Reset connection', async function () { + await conn.resetCache() + await conn.delete({ path: '/bookmarks/test', tree }) + }) + + after('Clean up', async function () { + await conn.delete({ path: '/bookmarks/test', tree }) + await conn.resetCache() + }) - for (let i = 0; i < 4; i++) { - describe(`Testing connection ${i + 1}`, async function() { - it(`1. Should error when neither 'url' nor 'path' are supplied`, async function() { - console.log( - `Cache: ${connections[i].cache ? true : false}; Websocket: ${ - connections[i].websocket ? true : false - }` - ); - await connections[i].resetCache(); - await connections[i].delete({ path: "/bookmarks/test", tree }); - await putResource({ something: "b" }, "/bookmarks/test"); + it(`1. Should error when neither 'url' nor 'path' are supplied`, async function () { + await putResource({ something: 'b' }, '/bookmarks/test') return expect( - connections[i].delete({ - type: "application/json", + conn.delete({ + type: 'application/json' }) - ).to.be.rejectedWith(Error, "Either path or url must be specified."); - }); + ).to.be.rejectedWith(Error, 'Either path or url must be specified.') + }) - it(`2. Shouldn't error when the 'Content-Type' header can be derived from the 'type' key`, async function() { - await connections[i].resetCache() - await connections[i].delete({path:'/bookmarks/test', tree}) - await putResource({'something': 'b'}, '/bookmarks/test') - var response = await connections[i].delete({ - path: '/bookmarks/test', - type: 'application/json' - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) + it(`2. Shouldn't error when the 'Content-Type' header can be derived from the 'type' key`, async function () { + await putResource({ something: 'b' }, '/bookmarks/test') + var response = await conn.delete({ + path: '/bookmarks/test', + type: 'application/json' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) - it(`3. Shouldn't error when 'Content-Type' header is specified.`, async function() { - await connections[i].resetCache() - await connections[i].delete({path:'/bookmarks/test', tree}) - await putResource({'something': 'b'}, '/bookmarks/test') - var response = await connections[i].delete({ - path: '/bookmarks/test', - headers: {'content-type': 'application/json'} - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) + it(`3. Shouldn't error when 'Content-Type' header is specified.`, async function () { + await putResource({ something: 'b' }, '/bookmarks/test') + var response = await conn.delete({ + path: '/bookmarks/test', + headers: { 'content-type': 'application/json' } + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) - it(`6. Should be able to delete only a resource and leave the link alone (GETs should 404)`, async function() { - this.timeout(5000); - await connections[i].resetCache(); - await connections[i].delete({ path: "/bookmarks/test", tree }); - var result = await putResource({ something: "b" }, "/bookmarks/test"); - var deleteResponse = await connections[i].delete({ - path: result.resource.headers["content-location"], - headers: { "content-type": "application/json" }, - }); + it(`6. Should be able to delete only a resource and leave the link alone (GETs should 404)`, async function () { + this.timeout(5000) + var result = await putResource({ something: 'b' }, '/bookmarks/test') + var deleteResponse = await conn.delete({ + path: result.resource.headers['content-location'], + headers: { 'content-type': 'application/json' } + }) return expect( - connections[i].get({ - path: "/bookmarks/test", + conn.get({ + path: '/bookmarks/test' }) - ).to.be.rejectedWith(Error, `Request failed with status code 404`); - }); + ).to.be.rejectedWith(Error, `Request failed with status code 404`) + }) - it(`7. Should allow us to delete only a link and leave the resource alone`, async function() { - this.timeout(40000); - await connections[i].resetCache() - await connections[i].delete({path:'/bookmarks/test', tree}) - await Promise.delay(3000); -// var result = await putResource({'something': 'b'}, '/bookmarks/test') - var result = await connections[i].put({ + it(`7. Should allow us to delete only a link and leave the resource alone`, async function () { + this.timeout(40000) + await Promise.delay(3000) + // var result = await putResource({'something': 'b'}, '/bookmarks/test') + var result = await conn.put({ path: '/bookmarks/test', - data: {'something': 'b'}, + data: { something: 'b' }, tree }) - await Promise.delay(3000); - var deleteResponse = await connections[i].delete({ - path: '/bookmarks/test', - headers: {'content-type': 'application/json'} - }) - expect(deleteResponse.status.toString().charAt(0)).to.equal('2'); - var response = await connections[i].get({ - path: result.headers['content-location'], - }) - expect(response.status.toString().charAt(0)).to.equal('2'); - }) + await Promise.delay(3000) + var deleteResponse = await conn.delete({ + path: '/bookmarks/test', + headers: { 'content-type': 'application/json' } + }) + expect(deleteResponse.status.toString().charAt(0)).to.equal('2') + var response = await conn.get({ + path: result.headers['content-location'] + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) - it(`8. Should handle two deletes in series`, async function() { - this.timeout(4000); - await connections[i].resetCache() - await connections[i].delete({path:'/bookmarks/test', tree}) - var result = await putResource({'something': 'b'}, '/bookmarks/test') - var deleteOne = await connections[i].delete({ - path: '/bookmarks/test', - headers: {'content-type': 'application/json'} - }) - expect(deleteOne.status.toString().charAt(0)).to.equal('2') - var deleteTwo = await connections[i].delete({ + it(`8. Should handle two deletes in series`, async function () { + this.timeout(4000) + var result = await putResource({ something: 'b' }, '/bookmarks/test') + var deleteOne = await conn.delete({ + path: '/bookmarks/test', + headers: { 'content-type': 'application/json' } + }) + expect(deleteOne.status.toString().charAt(0)).to.equal('2') + var deleteTwo = await conn.delete({ path: '/bookmarks/test', - headers: {'content-type': 'application/json'} + headers: { 'content-type': 'application/json' } }) expect(deleteTwo.status.toString().charAt(0)).to.equal('2') - }) + }) - it(`9. Should handle concurrent deletes, ultimately deleting the target endpoint as intended`, async function() { - this.timeout(4000); - await connections[i].resetCache() - await connections[i].delete({path:'/bookmarks/test', tree}) - var result = await putResource({'something': 'b'}, '/bookmarks/test') - var deleteOne = connections[i].delete({ - path: '/bookmarks/test', - headers: {'content-type': 'application/json'} - }) - var deleteTwo = connections[i].delete({ - path: '/bookmarks/test', - headers: {'content-type': 'application/json'} - }) - var deleteThree = connections[i].delete({ - path: '/bookmarks/test', - headers: {'content-type': 'application/json'} - }) - await Promise.join(deleteOne, deleteTwo, deleteThree, async function(deleteOne, deleteTwo, deleteThree) { - expect(deleteOne.status.toString().charAt(0)).to.equal('2') - expect(deleteTwo.status.toString().charAt(0)).to.equal('2') - expect(deleteThree.status.toString().charAt(0)).to.equal('2') - return expect(connections[i].get({ - path: '/bookmarks/test', - })).to.be.rejectedWith(Error, 'Request failed with status code 404'); - }) - }) + it(`9. Should handle concurrent deletes, ultimately deleting the target endpoint as intended`, async function () { + this.timeout(4000) + var result = await putResource({ something: 'b' }, '/bookmarks/test') + var deleteOne = conn.delete({ + path: '/bookmarks/test', + headers: { 'content-type': 'application/json' } + }) + var deleteTwo = conn.delete({ + path: '/bookmarks/test', + headers: { 'content-type': 'application/json' } + }) + var deleteThree = conn.delete({ + path: '/bookmarks/test', + headers: { 'content-type': 'application/json' } + }) + await Promise.join(deleteOne, deleteTwo, deleteThree, async function ( + deleteOne, + deleteTwo, + deleteThree + ) { + expect(deleteOne.status.toString().charAt(0)).to.equal('2') + expect(deleteTwo.status.toString().charAt(0)).to.equal('2') + expect(deleteThree.status.toString().charAt(0)).to.equal('2') + return expect( + conn.get({ + path: '/bookmarks/test' + }) + ).to.be.rejectedWith(Error, 'Request failed with status code 404') + }) + }) - it(`10. Should produce a 412 if the 'If-Match' precondition fails`, async function() { - this.timeout(4000); - await connections[i].resetCache(); - await connections[i].delete({ path: "/bookmarks/test", tree }); - var result = await putResource({ foo: "bar" }, "/bookmarks/test"); + it(`10. Should produce a 412 if the 'If-Match' precondition fails`, async function () { + this.timeout(4000) + var result = await putResource({ foo: 'bar' }, '/bookmarks/test') return expect( - connections[i].delete({ - path: "/bookmarks/test", + conn.delete({ + path: '/bookmarks/test', headers: { - "If-Match": "2-foobar", - "content-type": "application/json", - }, + 'If-Match': '2-foobar', + 'content-type': 'application/json' + } }) - ).to.be.rejectedWith(Error, "Request failed with status code 412"); - }); + ).to.be.rejectedWith(Error, 'Request failed with status code 412') + }) - it(`11. Should succeed in deleting a link if a valid 'If-Match' header which matches the current resource's _rev is supplied`, async function() { - var result = await putResource({something: 'else'}, '/bookmarks/test'); - var response = await connections[i].delete({ - path: '/bookmarks/test', - headers: { - 'if-match': result.link.headers['x-oada-rev'], - 'content-type': 'application/json' - } - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) + it(`11. Should succeed in deleting a link if a valid 'If-Match' header which matches the current resource's _rev is supplied`, async function () { + var result = await putResource({ something: 'else' }, '/bookmarks/test') + var response = await conn.delete({ + path: '/bookmarks/test', + headers: { + 'if-match': result.link.headers['x-oada-rev'], + 'content-type': 'application/json' + } + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) - it(`12. Should succeed in deleting a resource if a valid 'If-Match' header which matches the current resource's _rev is supplied`, async function() { - var result = await putResource({foo: 'bar'}, '/bookmarks/test'); - var response = await connections[i].delete({ - path: result.resource.headers['content-location'], - headers: { - 'if-match': result.resource.headers['x-oada-rev'], - 'content-type': 'application/json' - } - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) + it(`12. Should succeed in deleting a resource if a valid 'If-Match' header which matches the current resource's _rev is supplied`, async function () { + var result = await putResource({ foo: 'bar' }, '/bookmarks/test') + var response = await conn.delete({ + path: result.resource.headers['content-location'], + headers: { + 'if-match': result.resource.headers['x-oada-rev'], + 'content-type': 'application/json' + } + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) - it(`13. Should delete an entire tree of links and resources when the 'tree' option is supplied`, async function() { - //await connections[i].resetCache(); - //await connections[i].delete({path:'/bookmarks/test', tree}) - var test = await putResource({ foo: "bar" }, "/bookmarks/test"); - var aaa = await putResource({ woo: "bar" }, "/bookmarks/test/aaa"); - var bbb = await putResource({ boo: "bar" }, "/bookmarks/test/aaa/bbb"); + it(`13. Should delete an entire tree of links and resources when the 'tree' option is supplied`, async function () { + //await conn.resetCache(); + //await conn.delete({path:'/bookmarks/test', tree}) + var test = await putResource({ foo: 'bar' }, '/bookmarks/test') + var aaa = await putResource({ woo: 'bar' }, '/bookmarks/test/aaa') + var bbb = await putResource({ boo: 'bar' }, '/bookmarks/test/aaa/bbb') var ccc = await putResource( - { noo: "bar" }, - "/bookmarks/test/aaa/bbb/index-one/ccc" - ); - var response = await connections[i].delete({ + { noo: 'bar' }, + '/bookmarks/test/aaa/bbb/index-one/ccc' + ) + var response = await conn.delete({ path: '/bookmarks/test', tree }) expect(response.status.toString().charAt(0)).to.equal('2') - return Promise.each([ - test.resource.headers['content-location'], - aaa.link.headers['content-location'], - aaa.resource.headers['content-location'], - bbb.link.headers['content-location'], - bbb.resource.headers['content-location'], - ccc.link.headers['content-location'], - ccc.resource.headers['content-location'], - ], (path) => { - if (/^\/resources/.test(path)) { - return expect(connections[i].get({path})).to.be.rejectedWith(Error, 'Request failed with status code 403'); - } else { - return expect(connections[i].get({path})).to.be.rejectedWith(Error, 'Request failedf with status code 404'); - } - }).catch((error) => { - console.log(error); - }); - }); + return Promise.each( + [ + test.resource.headers['content-location'], + aaa.link.headers['content-location'], + aaa.resource.headers['content-location'], + bbb.link.headers['content-location'], + bbb.resource.headers['content-location'], + ccc.link.headers['content-location'], + ccc.resource.headers['content-location'] + ], + path => { + if (/^\/resources/.test(path)) { + return expect(conn.get({ path })).to.be.rejectedWith( + Error, + 'Request failed with status code 403' + ) + } else { + return expect(conn.get({ path })).to.be.rejectedWith( + Error, + 'Request failedf with status code 404' + ) + } + } + ).catch(error => { + console.log(error) + }) + }) - it(`14. Should gracefully handle a sequence of PUT, DELETE, PUT executed in series`, async function() { - this.timeout(4000); - var putOne = await connections[i].put({ - path: '/bookmarks/test/aaa', - tree, - data: {putOne: 'putOne'} - }) - expect(putOne.status.toString().charAt(0)).to.equal('2') + it(`14. Should gracefully handle a sequence of PUT, DELETE, PUT executed in series`, async function () { + this.timeout(4000) + var putOne = await conn.put({ + path: '/bookmarks/test/aaa', + tree, + data: { putOne: 'putOne' } + }) + expect(putOne.status.toString().charAt(0)).to.equal('2') - var putTwo = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb', - tree, - data: {putTwo: 'putTwo'} - }) - expect(putTwo.status.toString().charAt(0)).to.equal('2') + var putTwo = await conn.put({ + path: '/bookmarks/test/aaa/bbb', + tree, + data: { putTwo: 'putTwo' } + }) + expect(putTwo.status.toString().charAt(0)).to.equal('2') - var deleteOne = await connections[i].delete({ - path: '/bookmarks/test', - tree, - }) - expect(deleteOne.status.toString().charAt(0)).to.equal('2') + var deleteOne = await conn.delete({ + path: '/bookmarks/test', + tree + }) + expect(deleteOne.status.toString().charAt(0)).to.equal('2') - var putThree = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc', - tree, - data: {putThree: 'putThree'} - }) - expect(putThree.status.toString().charAt(0)).to.equal('2') + var putThree = await conn.put({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc', + tree, + data: { putThree: 'putThree' } + }) + expect(putThree.status.toString().charAt(0)).to.equal('2') - var getOne = await connections[i].get({ - path: '/bookmarks/test', - tree - }) - expect(getOne.status.toString().charAt(0)).to.equal('2') - expect(getOne.data.aaa).to.not.include.key('putOne') - expect(getOne.data.aaa.bbb).to.not.include.key('putTwo') - expect(getOne.data.aaa.bbb['index-one'].ccc).to.include.key('putThree') + var getOne = await conn.get({ + path: '/bookmarks/test', + tree + }) + expect(getOne.status.toString().charAt(0)).to.equal('2') + expect(getOne.data.aaa).to.not.include.key('putOne') + expect(getOne.data.aaa.bbb).to.not.include.key('putTwo') + expect(getOne.data.aaa.bbb['index-one'].ccc).to.include.key('putThree') }) - it(`15. Should gracefully handle a concurrent sequence of PUT, DELETE, PUT`, async function() { - this.timeout(17000); - await connections[i].resetCache(); - await connections[i].delete({ path: "/bookmarks/test", tree }); + it(`15. Should gracefully handle a concurrent sequence of PUT, DELETE, PUT`, async function () { + this.timeout(17000) try { - var getOne = await connections[i].get({ - path: "/bookmarks/test/aaa", - }); + var getOne = await conn.get({ + path: '/bookmarks/test/aaa' + }) } catch (err) { - expect(err.response.status).to.equal(404); + expect(err.response.status).to.equal(404) } - var putOne = connections[i].put({ - path: "/bookmarks/test/aaa", - tree, - data: { putOne: "putOne" }, - }); - var putTwo = connections[i].put({ - path: "/bookmarks/test/aaa/bbb", + var putOne = conn.put({ + path: '/bookmarks/test/aaa', tree, - data: { putTwo: "putTwo" }, - }); - var deleteOne = connections[i].delete({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc", + data: { putOne: 'putOne' } + }) + var putTwo = conn.put({ + path: '/bookmarks/test/aaa/bbb', tree, - }); - var putThree = connections[i].put({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc", + data: { putTwo: 'putTwo' } + }) + var deleteOne = conn.delete({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc', + tree + }) + var putThree = conn.put({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc', tree, - data: { putThree: "putThree" }, - }); + data: { putThree: 'putThree' } + }) var result = await Promise.join( putOne, putTwo, deleteOne, putThree, //async function(putOne,putTwo,deleteOne,putThree) { - async function(putOne,putTwo, deleteOne, putThree) { - expect(putOne.status.toString().charAt(0)).to.equal('2') - expect(putTwo.status.toString().charAt(0)).to.equal('2') - expect(deleteOne.status.toString().charAt(0)).to.equal('2') - expect(putThree.status.toString().charAt(0)).to.equal('2') - } + async function (putOne, putTwo, deleteOne, putThree) { + expect(putOne.status.toString().charAt(0)).to.equal('2') + expect(putTwo.status.toString().charAt(0)).to.equal('2') + expect(deleteOne.status.toString().charAt(0)).to.equal('2') + expect(putThree.status.toString().charAt(0)).to.equal('2') + } ) }) - - it("Now clean up", async function() { - await connections[i].delete({ path: "/bookmarks/test", tree }); - await connections[i].resetCache(); - }); - }); + }) } -}); +}) diff --git a/test/get.test.js b/test/get.test.js index 40cbe98..f402ce3 100644 --- a/test/get.test.js +++ b/test/get.test.js @@ -1,201 +1,311 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const pretty = require("prettyjson"); -const _ = require("lodash"); -const uuid = require("uuid"); -const chai = require("chai"); -var chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised); -var expect = chai.expect; -const axios = require("axios"); -const { token, domain } = require("./config"); -const { tree, putResource, getConnections } = require("./utils.js"); - -var connections; -var resources = []; - -describe(`------------GET-----------------`, async function() { - before(`Create connection types`, async function() { - connections = await getConnections({ - domain, - token: 'def', - }) - }) - - for (let i = 0; i < 4; i++) { - describe(`Testing connection ${i+1}`, async function() { - - it(`1. Should allow for a basic GET request without tree parameter`, async function() { - console.log(`Cache: ${connections[i].cache ? true : false}; Websocket: ${connections[i].websocket ? true : false}`) - this.timeout(4000); - await putResource({ - _type: 'application/vnd.oada.notes.1+json', - sometest: 'abc' - }, - domain+'/bookmarks/test') - - await putResource({'somethingelse': 'okay'}, domain+'/bookmarks/test/aaa') - await putResource({'b': 'b'}, domain+'/bookmarks/test/aaa/bbb') - await putResource({'c': 'c'}, domain+'/bookmarks/test/aaa/bbb/index-one/ccc') - await putResource({'d': 'd'}, domain+'/bookmarks/test/aaa/bbb/index-one/ccc/index-two/bob') - await putResource({'e': 'e'}, domain+'/bookmarks/test/aaa/bbb/index-one/ccc/index-two/bob/index-three/2018') - - var test = await connections[i].get({ - path: '/bookmarks/test' - }) - expect(test.data).to.include.key('aaa') - expect(test.data).to.include.keys(['_id', '_meta', '_type', '_rev']) - }) - - it(`2. Should allow you to get a resource directly`, async function() { - var response = await connections[i].get({ - path: '/resources/default:resources_bookmarks_321', - }) - expect(response.data).to.include.keys(['_id', '_rev', '_meta']) - }) - - it(`3. Should error when the root path of a 'tree' GET doesn't exist`, async function() { - return expect(connections[i].get({ - path: '/bookmarks/test/testTwo', - tree - })).to.be.rejectedWith(Error, 'Request failed with status code 404'); - }) - - it(`4. Should handle when the cache only contains part of the tree which is on the server`, async function() { - if (connections[i].cache) { - await connections[i].resetCache() - var subTree = _.cloneDeep(tree); - delete subTree.bookmarks.test.aaa.bbb['index-one']['*'] - // Prep the cache with part of the tree - var first = await connections[i].get({ - path: '/bookmarks/test', - tree: subTree - }) - expect(first.data).to.include.key('aaa') - expect(first.data['aaa']).to.include.key('bbb') - expect(first.data['aaa']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(first.data['aaa']['bbb']).to.include.key('index-one') - expect(first.data['aaa']['bbb']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(first.data['aaa']['bbb']['index-one']).to.not.include.keys(['_id', '_meta', '_type', '_rev']) - expect(first.data['aaa']['bbb']['index-one']).to.include.key('ccc') - expect(first.data['aaa']['bbb']['index-one']['ccc']).to.have.keys(['_id','_rev']) - - // Now Attempt to GET the entire tree - var second = await connections[i].get({ - path: '/bookmarks/test', - tree - }) - expect(second.data).to.include.key('aaa') - expect(second.data['aaa']).to.include.key('bbb') - expect(second.data['aaa']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(second.data['aaa']['bbb']).to.include.key('index-one') - expect(second.data['aaa']['bbb']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(second.data['aaa']['bbb']['index-one']).to.not.include.keys(['_id', '_meta', '_type', '_rev']) - expect(second.data['aaa']['bbb']['index-one']).to.include.key('ccc') - expect(second.data['aaa']['bbb']['index-one']['ccc']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(second.data['aaa']['bbb']['index-one']['ccc']['index-two']['bob']['index-three']['2018']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(second.cached).to.equal(false) - } - }) - - it(`5. Should handle fully cached tree`, async function() { - if (connections[i].cache) { - var test = await connections[i].get({ - path: '/bookmarks/test', - tree - }) - expect(test.data).to.include.key('aaa') - expect(test.data['aaa']).to.include.key('bbb') - expect(test.data['aaa']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(test.data['aaa']['bbb']).to.include.key('index-one') - expect(test.data['aaa']['bbb']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(test.data['aaa']['bbb']['index-one']).to.not.include.keys(['_id', '_meta', '_type', '_rev']) - expect(test.data['aaa']['bbb']['index-one']).to.include.key('ccc') - expect(test.data['aaa']['bbb']['index-one']['ccc']).to.include.keys(['_id', '_meta', '_type', '_rev']) - expect(test.cached).to.equal(true) - } - }) - - it(`6. Should only return the part of the tree prescribed by the given 'tree' when the server has more data`, async function() { - await connections[i].resetCache(); - try { - await connections[i].delete({path:'/bookmarks/test', tree}); - } catch (error) { - console.log(error) +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const _ = require('lodash') +const chai = require('chai') +const chaiAsPromised = require('chai-as-promised') +chai.use(chaiAsPromised) +const { expect } = chai +const axios = require('axios') +const { token, domain } = require('./config') +const { tree, putResource, getConnections } = require('./utils.js') + +const connections = getConnections({ + domain, + token +}) + +describe(`------------GET-----------------`, async function () { + for (const connection of connections) { + const { cache, websocket } = connection + describe(`Testing connection cache:${cache} websocket:${websocket}`, function () { + let conn + before('Wait on connection', async function () { + conn = await connection + }) + + beforeEach('Reset connection', async function () { + await conn.resetCache() + }) + + after('Clean up', async function () { + this.timeout(5000) + await conn.resetCache() + try { + await conn.delete({ path: '/bookmarks/test', tree }) + } catch (error) { + console.log(error) + } + }) + + it(`1. Should allow for a basic GET request without tree parameter`, async function () { + this.timeout(4000) + await putResource( + { + _type: 'application/vnd.oada.notes.1+json', + sometest: 'abc' + }, + domain + '/bookmarks/test' + ) + + await putResource( + { somethingelse: 'okay' }, + domain + '/bookmarks/test/aaa' + ) + await putResource({ b: 'b' }, domain + '/bookmarks/test/aaa/bbb') + await putResource( + { c: 'c' }, + domain + '/bookmarks/test/aaa/bbb/index-one/ccc' + ) + await putResource( + { d: 'd' }, + domain + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/bob' + ) + await putResource( + { e: 'e' }, + domain + + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/bob/index-three/2018' + ) + + var test = await conn.get({ + path: '/bookmarks/test' + }) + expect(test.data).to.include.key('aaa') + expect(test.data).to.include.keys(['_id', '_meta', '_type', '_rev']) + }) + + it(`2. Should allow you to get a resource directly`, async function () { + var response = await conn.get({ + path: '/resources/default:resources_bookmarks_321' + }) + expect(response.data).to.include.keys(['_id', '_rev', '_meta']) + }) + + it(`3. Should error when the root path of a 'tree' GET doesn't exist`, async function () { + return expect( + conn.get({ + path: '/bookmarks/test/testTwo', + tree + }) + ).to.be.rejectedWith(Error, 'Request failed with status code 404') + }) + + it(`4. Should handle when the cache only contains part of the tree which is on the server`, async function () { + if (conn.cache) { + var subTree = _.cloneDeep(tree) + delete subTree.bookmarks.test.aaa.bbb['index-one']['*'] + // Prep the cache with part of the tree + var first = await conn.get({ + path: '/bookmarks/test', + tree: subTree + }) + expect(first.data).to.include.key('aaa') + expect(first.data['aaa']).to.include.key('bbb') + expect(first.data['aaa']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(first.data['aaa']['bbb']).to.include.key('index-one') + expect(first.data['aaa']['bbb']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(first.data['aaa']['bbb']['index-one']).to.not.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(first.data['aaa']['bbb']['index-one']).to.include.key('ccc') + expect(first.data['aaa']['bbb']['index-one']['ccc']).to.have.keys([ + '_id', + '_rev' + ]) + + // Now Attempt to GET the entire tree + var second = await conn.get({ + path: '/bookmarks/test', + tree + }) + expect(second.data).to.include.key('aaa') + expect(second.data['aaa']).to.include.key('bbb') + expect(second.data['aaa']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(second.data['aaa']['bbb']).to.include.key('index-one') + expect(second.data['aaa']['bbb']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(second.data['aaa']['bbb']['index-one']).to.not.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(second.data['aaa']['bbb']['index-one']).to.include.key('ccc') + expect(second.data['aaa']['bbb']['index-one']['ccc']).to.include.keys( + ['_id', '_meta', '_type', '_rev'] + ) + expect( + second.data['aaa']['bbb']['index-one']['ccc']['index-two']['bob'][ + 'index-three' + ]['2018'] + ).to.include.keys(['_id', '_meta', '_type', '_rev']) + expect(second.cached).to.equal(false) + } + }) + + it(`5. Should handle fully cached tree`, async function () { + if (conn.cache) { + await conn.get({ + path: '/bookmarks/test', + tree + }) + + var test = await conn.get({ + path: '/bookmarks/test', + tree + }) + expect(test.data).to.include.key('aaa') + expect(test.data['aaa']).to.include.key('bbb') + expect(test.data['aaa']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(test.data['aaa']['bbb']).to.include.key('index-one') + expect(test.data['aaa']['bbb']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(test.data['aaa']['bbb']['index-one']).to.not.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(test.data['aaa']['bbb']['index-one']).to.include.key('ccc') + expect(test.data['aaa']['bbb']['index-one']['ccc']).to.include.keys([ + '_id', + '_meta', + '_type', + '_rev' + ]) + expect(test.cached).to.equal(true) } - this.timeout(4000) - - await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/hhh', - data: {foo: "bar"}, - tree, - }) - - await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/hhh/index-two/bob/index-three/2014', - type: 'application/vnd.oada.yield.1+json', - data: {bar: "foo"}, - tree, - }) - - await axios({ - method: 'put', - url: domain+'/bookmarks/test/aaa/bbb/extraKey', - headers: {'Authorization': 'Bearer def', 'Content-Type': 'application/vnd.oada.yield.1+json'}, - data: {hello: "world"}, - }) - - await axios({ - method: 'put', - url: domain+'/resources/7656401651', - headers: {'Authorization': 'Bearer def', 'Content-Type': 'application/vnd.oada.yield.1+json'}, - data: {foobar: 'foobar'}, - }) - - await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/hhh/index-two/joe/somethingElse', - type: 'application/vnd.oada.yield.1+json', - data: {_id: "resources/7656401651"}, - tree, - }) - - var first = await connections[i].get({ - path: '/bookmarks/test', - tree, - }) - - expect(first.data).to.include.key('aaa') - expect(first.data['aaa']).to.include.key('bbb') - expect(first.data['aaa']).to.include.keys(['_id', '_type', '_rev']) - expect(first.data['aaa']['bbb']).to.include.key('index-one') - expect(first.data['aaa']['bbb']).to.include.keys(['_id', '_type', '_rev']) - expect(first.data['aaa']['bbb']['index-one']).to.not.include.keys(['_id', '_type', '_rev']) - expect(first.data['aaa']['bbb']['index-one']).to.include.key('hhh') - expect(first.data['aaa']['bbb']['index-one']['hhh']).to.include.keys(['_id', '_type', '_rev']) - expect(first.data['aaa']['bbb']['index-one']['hhh']['index-two']).to.include.keys(['bob', 'joe']) - expect(first.data['aaa']['bbb']['index-one']['hhh']['index-two']['joe']).to.include.keys(['somethingElse']) - expect(first.data['aaa']['bbb']['index-one']['hhh']['index-two']['joe']['somethingElse']).to.not.include.keys(['foobar']) }) - /* it('should permit a GET on users', async function() { + it(`6. Should only return the part of the tree prescribed by the given 'tree' when the server has more data`, async function () { + try { + await conn.delete({ path: '/bookmarks/test', tree }) + } catch (error) { + console.log(error) + } + + this.timeout(4000) + + await conn.put({ + path: '/bookmarks/test/aaa/bbb/index-one/hhh', + data: { foo: 'bar' }, + tree + }) + + await conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/hhh/index-two/bob/index-three/2014', + type: 'application/vnd.oada.yield.1+json', + data: { bar: 'foo' }, + tree + }) + + await axios({ + method: 'put', + url: domain + '/bookmarks/test/aaa/bbb/extraKey', + headers: { + Authorization: 'Bearer def', + 'Content-Type': 'application/vnd.oada.yield.1+json' + }, + data: { hello: 'world' } + }) + + await axios({ + method: 'put', + url: domain + '/resources/7656401651', + headers: { + Authorization: 'Bearer def', + 'Content-Type': 'application/vnd.oada.yield.1+json' + }, + data: { foobar: 'foobar' } + }) + + await conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/hhh/index-two/joe/somethingElse', + type: 'application/vnd.oada.yield.1+json', + data: { _id: 'resources/7656401651' }, + tree + }) + + var first = await conn.get({ + path: '/bookmarks/test', + tree + }) + + expect(first.data).to.include.key('aaa') + expect(first.data['aaa']).to.include.key('bbb') + expect(first.data['aaa']).to.include.keys(['_id', '_type', '_rev']) + expect(first.data['aaa']['bbb']).to.include.key('index-one') + expect(first.data['aaa']['bbb']).to.include.keys([ + '_id', + '_type', + '_rev' + ]) + expect(first.data['aaa']['bbb']['index-one']).to.not.include.keys([ + '_id', + '_type', + '_rev' + ]) + expect(first.data['aaa']['bbb']['index-one']).to.include.key('hhh') + expect(first.data['aaa']['bbb']['index-one']['hhh']).to.include.keys([ + '_id', + '_type', + '_rev' + ]) + expect( + first.data['aaa']['bbb']['index-one']['hhh']['index-two'] + ).to.include.keys(['bob', 'joe']) + expect( + first.data['aaa']['bbb']['index-one']['hhh']['index-two']['joe'] + ).to.include.keys(['somethingElse']) + expect( + first.data['aaa']['bbb']['index-one']['hhh']['index-two']['joe'][ + 'somethingElse' + ] + ).to.not.include.keys(['foobar']) + }) + + xit('should permit a GET on users', async function () { await conections[i].put({ - path: '/bookmarks/test/_meta/_permissions/users/default:users_audrey_999', + path: + '/bookmarks/test/_meta/_permissions/users/default:users_audrey_999', data: { read: true, write: true, owner: false } }) - })*/ - - it("clean up", async function() { - this.timeout(5000); - await connections[i].resetCache(); - try { - await connections[i].delete({ path: "/bookmarks/test", tree }); - } catch (error) { - console.log(error); - } - }); - }); + }) + }) } -}); +}) diff --git a/test/memorycache.test.js b/test/memorycache.test.js index d13bfd6..a5c072d 100644 --- a/test/memorycache.test.js +++ b/test/memorycache.test.js @@ -1,128 +1,122 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const _ = require("lodash"); -const oada = require("../src/index.js"); -process.env.NODE_TLS_REJECT_UNAUTHORIZED=0 -const uuid = require("uuid"); -const chai = require("chai"); -var chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised); -var expect = chai.expect; -const axios = require("axios"); -const { token, domain } = require("./config"); -const { tree, putResource, getConnections } = require("./utils.js"); -const { performance } = require("perf_hooks"); -console.log(oada); -oada.setDbPrefix("./test/test-data/"); - -const timer = ms => new Promise(res => setTimeout(res, ms)); - -const cleanMemoryTimer = 11000; -const dbPutDelay = 6000; -var connection; - -describe(`In-memory Cache`, async function() { - - this.timeout(30000); - describe(`GET`, async function() { - before(`Create connection`, async function() { +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const oada = require('../src/index.js') +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const chai = require('chai') +const chaiAsPromised = require('chai-as-promised') +chai.use(chaiAsPromised) +const { expect } = chai +const { token, domain } = require('./config') +const { tree } = require('./utils.js') +oada.setDbPrefix('./test/test-data/') + +const timer = ms => new Promise(res => setTimeout(res, ms)) + +const cleanMemoryTimer = 11000 +const dbPutDelay = 6000 +var connection + +describe(`In-memory Cache`, async function () { + this.timeout(30000) + describe(`GET`, async function () { + before(`Create connection`, async function () { try { connection = await oada.connect({ domain, - token, - }); + token + }) } catch (err) { console.log(err) } - }); + }) - it(`Should get a resource from server`, async function() { + it(`Should get a resource from server`, async function () { var response = await connection.get({ - path: "/resources/default:resources_bookmarks_321", - }); - expect(response.data).to.include.keys(["_id", "_rev", "_type", "_meta"]); - }); + path: '/resources/default:resources_bookmarks_321' + }) + expect(response.data).to.include.keys(['_id', '_rev', '_type', '_meta']) + }) - it(`In-memory cache should contain one entry`, async function() { + it(`In-memory cache should contain one entry`, async function () { expect(connection._getMemoryCache()).to.have.property( - "resources/default:resources_bookmarks_321" - ); - }); + 'resources/default:resources_bookmarks_321' + ) + }) - it(`Should wait ${cleanMemoryTimer} ms`, async function() { - await timer(cleanMemoryTimer); - }); + it(`Should wait ${cleanMemoryTimer} ms`, async function () { + await timer(cleanMemoryTimer) + }) - it(`In-memory cache should be empty`, async function() { + it(`In-memory cache should be empty`, async function () { console.log(connection._getMemoryCache()) - expect(connection._getMemoryCache()).to.be.empty; - }); + expect(connection._getMemoryCache()).to.be.empty + }) - it(`Should get a resource from PouchDB`, async function() { + it(`Should get a resource from PouchDB`, async function () { var response = await connection.get({ - path: "/resources/default:resources_bookmarks_321", - }); - expect(response.data).to.include.keys(["_id", "_rev", "_type", "_meta"]); - }); + path: '/resources/default:resources_bookmarks_321' + }) + expect(response.data).to.include.keys(['_id', '_rev', '_type', '_meta']) + }) - it(`In-memory cache should contain one entry`, async function() { + it(`In-memory cache should contain one entry`, async function () { expect(connection._getMemoryCache()).to.have.property( - "resources/default:resources_bookmarks_321" - ); - }); + 'resources/default:resources_bookmarks_321' + ) + }) - it(`Should get a resource from im-memory cache`, async function() { + it(`Should get a resource from im-memory cache`, async function () { var response = await connection.get({ - path: "/resources/default:resources_bookmarks_321", - }); - expect(response.data).to.include.keys(["_id", "_rev", "_type", "_meta"]); - }); - - after(`Close connection`, async function() { - await timer(cleanMemoryTimer); - connection.disconnect(); - }); - }); - - describe(`PUT`, async function() { - var connection; - before(`Create connection`, async function() { + path: '/resources/default:resources_bookmarks_321' + }) + expect(response.data).to.include.keys(['_id', '_rev', '_type', '_meta']) + }) + + after(`Close connection`, async function () { + await timer(cleanMemoryTimer) + connection.disconnect() + }) + }) + + describe(`PUT`, async function () { + let connection + before(`Create connection`, async function () { connection = await oada.connect({ domain, - token, - }); - }); + token + }) + }) - it(`Should create a resource`, async function() { - var response = await connection.put({ - path: "/bookmarks/test", + step(`Should create a resource`, async function () { + const response = await connection.put({ + path: '/bookmarks/test', data: { sometest: 123 }, - tree, - }); - expect(response.status.toString().charAt(0)).to.equal('2'); + tree + }) + expect(response.status.toString().charAt(0)).to.equal('2') expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); - - it(`The resource should be waiting for PUT`, async function() { - var data = connection._getMemoryCache(); - expect(data[Object.keys(data)[0]]).to.include.keys(["promise"]); - }); - - it(`The resource should NOT be waiting for PUT`, async function() { - await timer(dbPutDelay); - var data = connection._getMemoryCache(); - expect(data[Object.keys(data)[0]].putPending).to.be.false; - }); - - it(`In-memory cache should be empty`, async function() { - await timer(cleanMemoryTimer); - expect(connection._getMemoryCache()).to.be.empty; - }); - - after(`Close connection`, async function() { - connection.disconnect(); - }); - }); -}); + 'content-location', + 'x-oada-rev' + ]) + }) + + step(`The resource should be waiting for PUT`, async function () { + var data = connection._getMemoryCache() + expect(data[Object.keys(data)[0]]).to.include.keys(['promise']) + }) + + step(`The resource should NOT be waiting for PUT`, async function () { + await timer(dbPutDelay) + var data = connection._getMemoryCache() + expect(data[Object.keys(data)[0]].putPending).to.be.false + }) + + step(`In-memory cache should be empty`, async function () { + await timer(cleanMemoryTimer) + expect(connection._getMemoryCache()).to.be.empty + }) + + after(`Close connection`, async function () { + connection.disconnect() + }) + }) +}) diff --git a/test/mocha.opts b/test/mocha.opts deleted file mode 100644 index 6edafe7..0000000 --- a/test/mocha.opts +++ /dev/null @@ -1 +0,0 @@ ---file ./test/setup-all-tests.js diff --git a/test/put.test.js b/test/put.test.js index 7ec911a..8bab7b5 100644 --- a/test/put.test.js +++ b/test/put.test.js @@ -1,631 +1,815 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const axios = require("axios"); -const pretty = require("prettyjson"); -const Promise = require("bluebird"); -const _ = require("lodash"); -var chai = require("chai"); -var chaiAsPromised = require("chai-as-promised"); -chai.use(chaiAsPromised); -var expect = chai.expect; -const { token, domain } = require("./config.js"); -const { tree, getConnections } = require("./utils.js"); - -var resources = []; -var connections; - -describe(`------------PUT-----------------`, async function() { - before(`Create connection types`, async function() { - connections = await getConnections({ - domain, - token: 'def', - }) - }) - - for (let i = 0; i < 4; i++) { - describe(`Testing connection ${i+1}`, async function() { - it(`1. Should error when neither 'url' nor 'path' are supplied`, async function() { - console.log(`Cache: ${connections[i].cache ? true : false}; Websocket: ${connections[i].websocket ? true : false}`) - return expect(connections[i].put({ - data: `"abc123"`, - tree, - type: 'application/json' - })).to.be.rejectedWith(Error, 'Either path or url must be specified.') - }) - - it(`2. Shouldn't error when the 'Content-Type' header can be derived from the '_type' key in the PUT body`, async function() { - var response = await connections[i].put({ - path: '/bookmarks/test/sometest', - data: { _type: 'application/json'}, - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) - - it(`3. Shouldn't error when the 'Content-Type' header can be derived from the 'type' key`, async function() { - var response = await connections[i].put({ - path: '/bookmarks/test/somethingnew', - data: `"abc123"`, - type: 'application/json' - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) - - it(`4. Shouldn't error when 'Content-Type' header is specified.`, async function() { - var response = await connections[i].put({ - path: '/bookmarks/test/somethingnew', - data: `"abc123"`, - headers: {'Content-Type': 'application/json'} - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) - - it(`5. Shouldn't error when 'Content-Type' header (_type) can be derived from the 'tree'`, async function() { - this.timeout(5000); - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - - var response = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/sometest', - tree, - data: `"abc123"`, - }).catch((err) => { - console.log('ERRRRRRRRRRRRRRR', err); - }) - expect(response.status.toString().charAt(0)).to.equal('2') - }) - - it(`6. Should error when _type cannot be derived from the above tested sources`, async function() { +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const pretty = require('prettyjson') +const Promise = require('bluebird') +const _ = require('lodash') +var chai = require('chai') +var chaiAsPromised = require('chai-as-promised') +chai.use(chaiAsPromised) +var expect = chai.expect +const { token, domain } = require('./config.js') +const { tree, getConnections } = require('./utils.js') + +const connections = getConnections({ + domain, + token +}) + +describe(`------------PUT-----------------`, async function () { + for (const connection of connections) { + const { cache, websocket } = connection + describe(`Testing connection cache:${cache} websocket:${websocket}`, function () { + let conn + before('Wait on connection', async function () { + conn = await connection + }) + + beforeEach('Reset connection', async function () { + await conn.resetCache() + await conn.delete({ path: '/bookmarks/test', tree }) + }) + + after(`Clean up`, async function () { + this.timeout(3000) + await conn.delete({ path: '/bookmarks/test', tree }) + await conn.resetCache() + }) + + it(`1. Should error when neither 'url' nor 'path' are supplied`, async function () { return expect( - connections[i].put({ - path: "/bookmarks/test/sometest", + conn.put({ data: `"abc123"`, + tree, + type: 'application/json' }) - ).to.be.rejectedWith(Error, `content-type header must be specified.`); - }); + ).to.be.rejectedWith(Error, 'Either path or url must be specified.') + }) + + it(`2. Shouldn't error when the 'Content-Type' header can be derived from the '_type' key in the PUT body`, async function () { + var response = await conn.put({ + path: '/bookmarks/test/sometest', + data: { _type: 'application/json' } + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) - it(`7. Should produce a 403 error when using a content-type header for which your token does not have access to read/write`, async function() { - this.timeout(4000); - await connections[i].delete({ path: "/bookmarks/test", tree }); - await connections[i].resetCache(); + it(`3. Shouldn't error when the 'Content-Type' header can be derived from the 'type' key`, async function () { + var response = await conn.put({ + path: '/bookmarks/test/somethingnew', + data: `"abc123"`, + type: 'application/json' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) + + it(`4. Shouldn't error when 'Content-Type' header is specified.`, async function () { + var response = await conn.put({ + path: '/bookmarks/test/somethingnew', + data: `"abc123"`, + headers: { 'Content-Type': 'application/json' } + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) + + it(`5. Shouldn't error when 'Content-Type' header (_type) can be derived from the 'tree'`, async function () { + this.timeout(5000) + + var response = await conn + .put({ + path: '/bookmarks/test/aaa/bbb/index-one/sometest', + tree, + data: `"abc123"` + }) + .catch(err => { + console.log('ERRRRRRRRRRRRRRR', err) + }) + expect(response.status.toString().charAt(0)).to.equal('2') + }) + + it(`6. Should error when _type cannot be derived from the above tested sources`, async function () { return expect( - connections[i].put({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc", - headers: { "Content-Type": "application/vnd.oada.foobar.1+json" }, + conn.put({ + path: '/bookmarks/test/sometest', + data: `"abc123"` + }) + ).to.be.rejectedWith(Error, `content-type header must be specified.`) + }) + + it(`7. Should produce a 403 error when using a content-type header for which your token does not have access to read/write`, async function () { + this.timeout(4000) + + return expect( + conn.put({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc', + headers: { 'Content-Type': 'application/vnd.oada.foobar.1+json' }, tree: tree, - data: { anothertest: 123 }, + data: { anothertest: 123 } }) - ).to.be.rejectedWith(Error, "Request failed with status code 403"); - }); - - it(`9. Should properly create a single new resource. The link _rev should not remain as 0`, async function() { - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var response = await connections[i].put({ - path: '/bookmarks/test', - data: {'sometest': 123}, - tree, - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - response = await connections[i].get({ - path: '/bookmarks', - tree - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'test']) + ).to.be.rejectedWith(Error, 'Request failed with status code 403') + }) + + it(`9. Should properly create a single new resource. The link _rev should not remain as 0`, async function () { + var response = await conn.put({ + path: '/bookmarks/test', + data: { sometest: 123 }, + tree + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + response = await conn.get({ + path: '/bookmarks', + tree + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'test']) expect(response.data.test._rev).not.to.equal(0) - }) - - it(`10. Should create the proper resource breaks on the server when a 'tree' parameter is supplied to a deep endpoint`, async function() { - this.timeout(4000) - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var response = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', - data: {"test": "some test"}, - tree, - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - response = await connections[i].get({ - path: '/bookmarks/test/aaa', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa', - }) - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'bbb']) - expect(response.data.bbb).to.have.keys(['_id', '_rev']) - expect(response.data.bbb).to.not.include.keys(['index-one']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'index-one']) - expect(response.data['index-one']).to.not.include.keys(['_id', '_rev']) - expect(response.data['index-one']).to.include.keys(['ccc']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.include.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['ccc']) - expect(response.data.ccc).to.have.keys(['_id', '_rev']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_type', '_rev', 'index-two']) - expect(response.data['index-two']).to.not.include.keys(['_id', '_rev']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.include.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['ddd']) - expect(response.data['ddd']).to.have.keys(['_id', '_rev']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_type', '_rev', 'index-three']) - expect(response.data['index-three']).to.not.include.keys(['_id', '_rev']) - expect(response.data['index-three']).to.include.keys(['eee']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.include.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['eee']) - expect(response.data['eee']).to.have.keys(['_id']) - expect(response.data['eee']).to.not.have.keys(['_rev']) - - await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', - }) - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - if (connections[i].cache) { - expect(response.cached).to.equal(true) - } - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'test']) - expect(response.data['test']).to.not.include.keys(['_id', '_rev']) - }) - - it(`11. Should allow for a PUT request without a 'tree' parameter`, async function() { - this.timeout(4000) - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var response = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee/test/123', - type: 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', - data: `"some test"`, - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - - response = await connections[i].get({ - path: '/bookmarks/test', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.have.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['aaa']) - expect(response.data.aaa).to.not.have.keys(['_id', '_rev']) - expect(response.data.aaa).to.include.keys(['bbb']) - }) - - it(`12. Should create the proper resource if we PUT to a different path on an existing subtree`, async function() { - this.timeout(6000); - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var response = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg/index-three/hhh/test/123', - type: 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', - data: `"some test"`, - tree, - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - response = await connections[i].get({ - path: '/bookmarks/test', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'aaa']) - expect(response.data.aaa).to.have.keys(['_id', '_rev']) - expect(response.data.aaa).to.not.include.keys(['bbb']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'bbb']) - expect(response.data.bbb).to.have.keys(['_id', '_rev']) - expect(response.data.bbb).to.not.include.keys(['index-one']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'index-one']) - expect(response.data['index-one']).to.not.include.keys(['_id', '_rev']) - expect(response.data['index-one']).to.include.keys(['ccc']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.include.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['ccc']) - expect(response.data.ccc).to.have.keys(['_id', '_rev']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'index-two']) - expect(response.data['index-two']).to.not.include.keys(['_id', '_rev']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.include.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['ggg']) - expect(response.data['ggg']).to.have.keys(['_id', '_rev']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'index-three']) - expect(response.data['index-three']).to.not.include.keys(['_id', '_rev']) - expect(response.data['index-three']).to.include.keys(['hhh']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg/index-three', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.not.include.keys(['_id', '_rev']) - expect(response.data).to.include.keys(['hhh']) - expect(response.data['hhh']).to.have.keys(['_id']) - expect(response.data['hhh']).to.not.have.keys(['_rev']) - - response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg/index-three/hhh', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'test']) - expect(response.data['test']).to.not.include.keys(['_id', '_rev']) - }) - - it(`13. Should use an _id specified via the 'tree'`, async function() { - this.timeout(4000) - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var newTree = _.cloneDeep(tree) - newTree.bookmarks.test.aaa.sss = { - _id: 'resources/sssssssss', - _type: 'application/vnd.oada.yield.1+json', - _rev: 0 - } - var putResponse = await connections[i].put({ - path: '/bookmarks/test/aaa/sss', - tree: newTree, - data: {anothertest: 123}, - }) - var response = await connections[i].get({ - path: '/bookmarks/test/aaa/sss', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'anothertest']) - expect(response.data._id).to.equal('resources/sssssssss') - }) - - it(`14. Should use an _id specified via the 'data'`, async function() { - this.timeout(4000) - var putResponse = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb', - tree: tree, - data: {_id: 'resources/foobar_foobar', sometest: 123}, - }) - var response = await connections[i].get({ - path: '/bookmarks/test/aaa/bbb', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', 'sometest']) - expect(response.data._id).to.equal('resources/foobar_foobar') - expect(response.data.sometest).to.equal(123) - }) - - it(`15. Should make unversioned links where _rev is not specified on resources`, async function() { - this.timeout(4000) - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - + }) + + it(`10. Should create the proper resource breaks on the server when a 'tree' parameter is supplied to a deep endpoint`, async function () { + this.timeout(4000) + + var response = await conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', + data: { test: 'some test' }, + tree + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + response = await conn.get({ + path: '/bookmarks/test/aaa' + }) + response = await conn.get({ + path: '/bookmarks/test/aaa' + }) + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'bbb']) + expect(response.data.bbb).to.have.keys(['_id', '_rev']) + expect(response.data.bbb).to.not.include.keys(['index-one']) + + await conn.get({ + path: '/bookmarks/test/aaa/bbb' + }) + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'index-one']) + expect(response.data['index-one']).to.not.include.keys(['_id', '_rev']) + expect(response.data['index-one']).to.include.keys(['ccc']) + + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one' + }) + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.include.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['ccc']) + expect(response.data.ccc).to.have.keys(['_id', '_rev']) + + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc' + }) + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys([ + '_id', + '_type', + '_rev', + 'index-two' + ]) + expect(response.data['index-two']).to.not.include.keys(['_id', '_rev']) + + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two' + }) + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.include.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['ddd']) + expect(response.data['ddd']).to.have.keys(['_id', '_rev']) + + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd' + }) + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys([ + '_id', + '_type', + '_rev', + 'index-three' + ]) + expect(response.data['index-three']).to.not.include.keys([ + '_id', + '_rev' + ]) + expect(response.data['index-three']).to.include.keys(['eee']) + + await conn.get({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three' + }) + response = await conn.get({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.include.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['eee']) + expect(response.data['eee']).to.have.keys(['_id']) + expect(response.data['eee']).to.not.have.keys(['_rev']) + + await conn.get({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee' + }) + response = await conn.get({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + if (conn.cache) { + expect(response.cached).to.equal(true) + } + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'test']) + expect(response.data['test']).to.not.include.keys(['_id', '_rev']) + }) + + it(`11. Should allow for a PUT request without a 'tree' parameter`, async function () { + this.timeout(4000) + + var response = await conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee/test/123', + type: + 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', + data: `"some test"` + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + + response = await conn.get({ + path: '/bookmarks/test' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.have.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['aaa']) + expect(response.data.aaa).to.not.have.keys(['_id', '_rev']) + expect(response.data.aaa).to.include.keys(['bbb']) + }) + + it(`12. Should create the proper resource if we PUT to a different path on an existing subtree`, async function () { + this.timeout(6000) + + var response = await conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg/index-three/hhh/test/123', + type: + 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', + data: `"some test"`, + tree + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + response = await conn.get({ + path: '/bookmarks/test' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'aaa']) + expect(response.data.aaa).to.have.keys(['_id', '_rev']) + expect(response.data.aaa).to.not.include.keys(['bbb']) + + response = await conn.get({ + path: '/bookmarks/test/aaa' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'bbb']) + expect(response.data.bbb).to.have.keys(['_id', '_rev']) + expect(response.data.bbb).to.not.include.keys(['index-one']) + + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'index-one']) + expect(response.data['index-one']).to.not.include.keys(['_id', '_rev']) + expect(response.data['index-one']).to.include.keys(['ccc']) + + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.include.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['ccc']) + expect(response.data.ccc).to.have.keys(['_id', '_rev']) + + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'index-two']) + expect(response.data['index-two']).to.not.include.keys(['_id', '_rev']) + + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.include.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['ggg']) + expect(response.data['ggg']).to.have.keys(['_id', '_rev']) + + response = await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'index-three']) + expect(response.data['index-three']).to.not.include.keys([ + '_id', + '_rev' + ]) + expect(response.data['index-three']).to.include.keys(['hhh']) + + response = await conn.get({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg/index-three' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.not.include.keys(['_id', '_rev']) + expect(response.data).to.include.keys(['hhh']) + expect(response.data['hhh']).to.have.keys(['_id']) + expect(response.data['hhh']).to.not.have.keys(['_rev']) + + response = await conn.get({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ggg/index-three/hhh' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'test']) + expect(response.data['test']).to.not.include.keys(['_id', '_rev']) + }) + + it(`13. Should use an _id specified via the 'tree'`, async function () { + this.timeout(4000) + var newTree = _.cloneDeep(tree) - delete newTree.bookmarks.test.aaa.bbb._rev - - var putResponse = await connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/', - tree: newTree, - data: {anothertest: 123}, - }) - var response = await connections[i].get({ - path: '/bookmarks/test/aaa', - }) - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data.bbb).to.include.keys(['_id']) - expect(response.data.bbb).to.not.include.keys(['_rev']) - }) - - it(`16. Should produce a 412 if the 'If-Match' header doesn't match the existing _rev`, async function() { - this.timeout(4000); - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - return expect(connections[i].put({ - path: '/bookmarks/test', - data: {'sometest': 'foobar'}, - headers: { - 'If-Match': '2-foobar', - 'Content-Type': 'application/json' - } - })).to.be.rejectedWith(Error, 'Request failed with status code 412'); - }) - - it(`17. Should produce a 412 if two PUTs are executed in series with the same 'If-Match' header`, async function() { - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var response = await connections[i].get({path: '/bookmarks'}) - expect(response.status.toString().charAt(0)).to.equal('2') - var putOne = await connections[i].put({ - path: '/bookmarks/test', - data: {'testOne': 'putOne'}, - headers: { - 'If-Match': response.headers['x-oada-rev'], - 'Content-Type': 'application/json' - } - }) - expect(putOne.status.toString().charAt(0)).to.equal('2') - return expect(connections[i].put({ - path: '/bookmarks/test', - data: {'testTwo': 'putTwo'}, - headers: { - 'If-Match': response.headers['x-oada-rev'], - 'Content-Type': 'application/json' - } - })).to.be.rejectedWith(Error, 'Request failed with status code 412'); + newTree.bookmarks.test.aaa.sss = { + _id: 'resources/sssssssss', + _type: 'application/vnd.oada.yield.1+json', + _rev: 0 + } + await conn.put({ + path: '/bookmarks/test/aaa/sss', + tree: newTree, + data: { anothertest: 123 } + }) + var response = await conn.get({ + path: '/bookmarks/test/aaa/sss' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'anothertest']) + expect(response.data._id).to.equal('resources/sssssssss') + }) + + it(`14. Should use an _id specified via the 'data'`, async function () { + this.timeout(4000) + + await conn.put({ + path: '/bookmarks/test/aaa/bbb', + tree: tree, + data: { _id: 'resources/foobar_foobar', sometest: 123 } + }) + var response = await conn.get({ + path: '/bookmarks/test/aaa/bbb' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', 'sometest']) + expect(response.data._id).to.equal('resources/foobar_foobar') + expect(response.data.sometest).to.equal(123) + }) + + it(`15. Should make unversioned links where _rev is not specified on resources`, async function () { + this.timeout(4000) + + var newTree = _.cloneDeep(tree) + delete newTree.bookmarks.test.aaa.bbb._rev + + await conn.put({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc/', + tree: newTree, + data: { anothertest: 123 } + }) + var response = await conn.get({ + path: '/bookmarks/test/aaa' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data.bbb).to.include.keys(['_id']) + expect(response.data.bbb).to.not.include.keys(['_rev']) + }) + + it(`16. Should produce a 412 if the 'If-Match' header doesn't match the existing _rev`, async function () { + this.timeout(4000) + + return expect( + conn.put({ + path: '/bookmarks/test', + data: { sometest: 'foobar' }, + headers: { + 'If-Match': '2-foobar', + 'Content-Type': 'application/json' + } + }) + ).to.be.rejectedWith(Error, 'Request failed with status code 412') + }) + + it(`17. Should produce a 412 if two PUTs are executed in series with the same 'If-Match' header`, async function () { + var response = await conn.get({ path: '/bookmarks' }) + expect(response.status.toString().charAt(0)).to.equal('2') + var putOne = await conn.put({ + path: '/bookmarks/test', + data: { testOne: 'putOne' }, + headers: { + 'If-Match': response.headers['x-oada-rev'], + 'Content-Type': 'application/json' + } + }) + expect(putOne.status.toString().charAt(0)).to.equal('2') + return expect( + conn.put({ + path: '/bookmarks/test', + data: { testTwo: 'putTwo' }, + headers: { + 'If-Match': response.headers['x-oada-rev'], + 'Content-Type': 'application/json' + } + }) + ).to.be.rejectedWith(Error, 'Request failed with status code 412') }) - it(`18. Should produce a 412 if two PUTs are executed in parallel with the same 'If-Match' header`, async function() { - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var response = await connections[i].get({path: '/bookmarks'}) - expect(response.status.toString().charAt(0)).to.equal('2') - var putOne = connections[i].put({ - path: '/bookmarks/test', - data: {'testOne': 'putOne'}, - headers: { - 'If-Match': response.headers['x-oada-rev'], - 'Content-Type': 'application/json' - } - }) - var putTwo = connections[i].put({ - path: '/bookmarks/test', - data: {'testTwo': 'putTwo'}, - headers: { - 'If-Match': response.headers['x-oada-rev'], - 'Content-Type': 'application/json' - } - }) - return expect( - Promise.join(putOne,putTwo, async function(putOne,putTwo) {}) - ).to.be.rejectedWith(Error, 'Request failed with status code 412') - }) - - it(`19. Should work under a sequence of PUT, DELETE, PUT`, async function() { - this.timeout(3000); - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var putOne = await connections[i].put({ + it(`18. Should produce a 412 if two PUTs are executed in parallel with the same 'If-Match' header`, async function () { + var response = await conn.get({ path: '/bookmarks' }) + expect(response.status.toString().charAt(0)).to.equal('2') + var putOne = conn.put({ + path: '/bookmarks/test', + data: { testOne: 'putOne' }, + headers: { + 'If-Match': response.headers['x-oada-rev'], + 'Content-Type': 'application/json' + } + }) + var putTwo = conn.put({ + path: '/bookmarks/test', + data: { testTwo: 'putTwo' }, + headers: { + 'If-Match': response.headers['x-oada-rev'], + 'Content-Type': 'application/json' + } + }) + return expect(Promise.join(putOne, putTwo)).to.be.rejectedWith( + Error, + 'Request failed with status code 412' + ) + }) + + it(`19. Should work under a sequence of PUT, DELETE, PUT`, async function () { + this.timeout(3000) + + var putOne = await conn.put({ path: '/bookmarks/test/aaa/bbb/index-one/ccc', tree, - data: {sometest: 123}, + data: { sometest: 123 } }) expect(putOne.status.toString().charAt(0)).to.equal('2') - var deleteOne = await connections[i].delete({ + var deleteOne = await conn.delete({ path: '/bookmarks/test/aaa/bbb/index-one/ccc', - tree, + tree }) expect(deleteOne.status.toString().charAt(0)).to.equal('2') - var putTwo = await connections[i].put({ + var putTwo = await conn.put({ path: '/bookmarks/test/aaa/bbb/index-one/ccc', tree, - data: {anothertest: 123}, + data: { anothertest: 123 } }) expect(putTwo.status.toString().charAt(0)).to.equal('2') - var response = await connections[i].get({ + var response = await conn.get({ path: '/bookmarks/test', tree }) - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']); - expect(response.data).to.include.keys(['_id', '_rev', '_type', 'aaa']); - expect(response.data.aaa).to.include.keys(['_id', '_rev', 'bbb', '_type']); - expect(response.data.aaa.bbb).to.include.keys(['_id', '_rev', 'index-one', '_type']); - expect(response.data.aaa.bbb['index-one']).to.include.keys(['ccc']); - expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys(['_id', '_rev', '_type', 'anothertest']); - expect(response.data.aaa.bbb['index-one'].ccc).to.not.include.keys(['sometest']); - }) - - it(`20. Should work under a sequence of PUTs to similar (same parent tree) endpoints`, async function() { - this.timeout(35000); - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); - var putOne = connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', - tree: tree, - data: {testOne: 123}, - }) - var putTwo = connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/fff/index-three/eee', - tree: tree, - data: {testTwo: 123}, - }) - var putThree = connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ggg/index-two/ddd/index-three/eee', - tree: tree, - data: {testThree: 123}, - }) - var putFour = connections[i].put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', - tree: tree, - data: {testFour: 123}, - }) - try{ - var result = await Promise.join(putOne,putTwo,putThree, putFour, async function(putOne,putTwo,putThree,putFour) { - var response = await connections[i].get({ - path: '/bookmarks/test', - tree - }) - expect(putOne.status.toString().charAt(0)).to.equal('2') - expect(putTwo.status.toString().charAt(0)).to.equal('2') - expect(putThree.status.toString().charAt(0)).to.equal('2') - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.status.toString().charAt(0)).to.equal('2') - expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) - expect(response.data).to.include.keys(['_id', '_rev', '_type', 'aaa']) - expect(response.data.aaa).to.include.keys(['_id', '_rev', 'bbb', '_type']) - expect(response.data.aaa.bbb).to.include.keys(['_id', '_rev', 'index-one', '_type']) - expect(response.data.aaa.bbb['index-one']).to.include.keys(['ccc', 'ggg']) - expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys(['_id', '_rev', '_type', 'index-two']) - expect(response.data.aaa.bbb['index-one'].ggg).to.include.keys(['_id', '_rev', '_type', 'index-two']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two']).to.include.keys(['ddd', 'fff']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].ddd).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].fff).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testOne', 'testFour']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testTwo']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two']).to.include.keys(['ddd']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two'].ddd).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testThree']) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys(['_id', '_rev', '_type', 'aaa']) + expect(response.data.aaa).to.include.keys([ + '_id', + '_rev', + 'bbb', + '_type' + ]) + expect(response.data.aaa.bbb).to.include.keys([ + '_id', + '_rev', + 'index-one', + '_type' + ]) + expect(response.data.aaa.bbb['index-one']).to.include.keys(['ccc']) + expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys([ + '_id', + '_rev', + '_type', + 'anothertest' + ]) + expect(response.data.aaa.bbb['index-one'].ccc).to.not.include.keys([ + 'sometest' + ]) + }) + + it(`20. Should work under a sequence of PUTs to similar (same parent tree) endpoints`, async function () { + this.timeout(35000) + + var putOne = conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', + tree: tree, + data: { testOne: 123 } }) + var putTwo = conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/fff/index-three/eee', + tree: tree, + data: { testTwo: 123 } + }) + var putThree = conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ggg/index-two/ddd/index-three/eee', + tree: tree, + data: { testThree: 123 } + }) + var putFour = conn.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', + tree: tree, + data: { testFour: 123 } + }) + try { + await Promise.join(putOne, putTwo, putThree, putFour, async function ( + putOne, + putTwo, + putThree + ) { + var response = await conn.get({ + path: '/bookmarks/test', + tree + }) + expect(putOne.status.toString().charAt(0)).to.equal('2') + expect(putTwo.status.toString().charAt(0)).to.equal('2') + expect(putThree.status.toString().charAt(0)).to.equal('2') + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys([ + 'content-location', + 'x-oada-rev' + ]) + expect(response.data).to.include.keys([ + '_id', + '_rev', + '_type', + 'aaa' + ]) + expect(response.data.aaa).to.include.keys([ + '_id', + '_rev', + 'bbb', + '_type' + ]) + expect(response.data.aaa.bbb).to.include.keys([ + '_id', + '_rev', + 'index-one', + '_type' + ]) + expect(response.data.aaa.bbb['index-one']).to.include.keys([ + 'ccc', + 'ggg' + ]) + expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys([ + '_id', + '_rev', + '_type', + 'index-two' + ]) + expect(response.data.aaa.bbb['index-one'].ggg).to.include.keys([ + '_id', + '_rev', + '_type', + 'index-two' + ]) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'] + ).to.include.keys(['ddd', 'fff']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].fff + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd[ + 'index-three' + ].eee + ).to.include.keys(['_id', '_rev', '_type', 'testOne', 'testFour']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].fff[ + 'index-three' + ].eee + ).to.include.keys(['_id', '_rev', '_type', 'testTwo']) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'] + ).to.include.keys(['ddd']) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd[ + 'index-three' + ].eee + ).to.include.keys(['_id', '_rev', '_type', 'testThree']) + }) } catch (error) { - console.log("TWAS ERROR", error); + console.log('TWAS ERROR', error) } - }); + }) - it(`21. Should allow links to be created first, then puts to that path should create the resource`, async function() { - this.timeout(7000); - await connections[i].delete({ path: "/bookmarks/test", tree }); - await connections[i].resetCache(); + it(`21. Should allow links to be created first, then puts to that path should create the resource`, async function () { + this.timeout(7000) - let putOne = await connections[i].put({ - path: "/bookmarks/test", - type: "application/json", + let putOne = await conn.put({ + path: '/bookmarks/test', + type: 'application/json', data: { _id: 'resources/11111', _rev: 0 } }) expect(putOne.status.toString().charAt(0)).to.equal('2') - let putTwo = await connections[i].put({ - path: "/bookmarks/test", - type: "application/json", + let putTwo = await conn.put({ + path: '/bookmarks/test', + type: 'application/json', data: { 'test-One': 'bar' } }) expect(putTwo.status.toString().charAt(0)).to.equal('2') - var getOne = await connections[i].get({ + var getOne = await conn.get({ path: '/bookmarks/test' }) expect(getOne.status.toString().charAt(0)).to.equal('2') expect(getOne.data).to.include.keys(['_id', '_rev', 'test-One']) - var getTwo = await connections[i].get({ + var getTwo = await conn.get({ path: '/resources/11111' }) expect(getTwo.status.toString().charAt(0)).to.equal('2') expect(getTwo.data).to.include.keys(['_id', '_rev', 'test-One']) }) - it(`22. Should allow links to be created first. A future PUT to that resource id should be handled`, async function() { - this.timeout(7000); - await connections[i].delete({ path: "/bookmarks/test", tree }); - await connections[i].resetCache(); + it(`22. Should allow links to be created first. A future PUT to that resource id should be handled`, async function () { + this.timeout(7000) - let putOne = await connections[i].put({ - path: "/bookmarks/test", - type: "application/json", + let putOne = await conn.put({ + path: '/bookmarks/test', + type: 'application/json', data: { _id: 'resources/11111', _rev: 0 @@ -633,35 +817,29 @@ describe(`------------PUT-----------------`, async function() { }) expect(putOne.status.toString().charAt(0)).to.equal('2') - let putTwo = await connections[i].put({ - path: "/resources/11111", - type: "application/json", + let putTwo = await conn.put({ + path: '/resources/11111', + type: 'application/json', data: { 'test-One': 'bar' } }) expect(putTwo.status.toString().charAt(0)).to.equal('2') - - var getOne = await connections[i].get({ + + var getOne = await conn.get({ path: '/bookmarks/test' }) expect(getOne.status.toString().charAt(0)).to.equal('2') expect(getOne.data).to.include.keys(['_id', '_rev', 'test-One']) - var getTwo = await connections[i].get({ + var getTwo = await conn.get({ path: '/resources/11111' }) expect(getTwo.status.toString().charAt(0)).to.equal('2') expect(getTwo.data).to.include.keys(['_id', '_rev', 'test-One']) console.log(pretty.render(getOne.data)) - console.log(pretty.render(getTwo.data)); - }); - - it(`23. Now clean up`, async function() { - this.timeout(3000); - await connections[i].delete({path:'/bookmarks/test', tree}) - await connections[i].resetCache(); + console.log(pretty.render(getTwo.data)) }) - }); + }) } -}); +}) diff --git a/test/startup.test.js b/test/startup.test.js index 8885c6a..a679fc4 100644 --- a/test/startup.test.js +++ b/test/startup.test.js @@ -1,522 +1,472 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const oada = require("../src/index"); -const chai = require("chai"); -var expect = chai.expect; -const status = require("http-status"); -oada.setDbPrefix("./test/test-data/"); - -const { token, domain } = require("./config.js"); -let connections = new Array(4); -let contentType = "application/vnd.oada.yield.1+json"; -let connectTime = 30 * 1000; // seconds to click through oauth -let nTests = 5; - -const _ = require("lodash"); -const config = require("./config.js"); -const { tree, getConnections } = require("./utils.js"); - -let connection; - -let connectionParameters = { +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const oada = require('../src/index') +const chai = require('chai') +const { expect } = chai +oada.setDbPrefix('./test/test-data/') + +const { domain } = require('./config.js') +const connections = new Array(4) +const connectTime = 30 * 1000 // seconds to click through oauth + +const { tree } = require('./utils.js') + +const connectionParameters = { domain, options: { - redirect: "http://localhost:8000/oauth2/redirect.html", + redirect: 'http://localhost:8000/oauth2/redirect.html', metadata: - "eyJqa3UiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbS9jZXJ0cyIsImtpZCI6ImtqY1NjamMzMmR3SlhYTEpEczNyMTI0c2ExIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJyZWRpcmVjdF91cmlzIjpbImh0dHA6Ly92aXAzLmVjbi5wdXJkdWUuZWR1OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiLCJodHRwOi8vbG9jYWxob3N0OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2QiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1iZWFyZXIiLCJncmFudF90eXBlcyI6WyJpbXBsaWNpdCJdLCJyZXNwb25zZV90eXBlcyI6WyJ0b2tlbiIsImlkX3Rva2VuIiwiaWRfdG9rZW4gdG9rZW4iXSwiY2xpZW50X25hbWUiOiJPcGVuQVRLIiwiY2xpZW50X3VyaSI6Imh0dHBzOi8vdmlwMy5lY24ucHVyZHVlLmVkdSIsImNvbnRhY3RzIjpbIlNhbSBOb2VsIDxzYW5vZWxAcHVyZHVlLmVkdT4iXSwic29mdHdhcmVfaWQiOiIxZjc4NDc3Zi0zNTQxLTQxM2ItOTdiNi04NjQ0YjRhZjViYjgiLCJyZWdpc3RyYXRpb25fcHJvdmlkZXIiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbSIsImlhdCI6MTUxMjAwNjc2MX0.AJSjNlWX8UKfVh-h1ebCe0MEGqKzArNJ6x0nmta0oFMcWMyR6Cn2saR-oHvU8WrtUMEr-w020mAjvhfYav4EdT3GOGtaFgnbVkIs73iIMtr8Z-Y6mDEzqRzNzVRMLghj7CyWRCNJEk0jwWjOuC8FH4UsfHmtw3ouMFomjwsNLY0", - scope: "oada.yield:all", - }, -}; - -describe("~~~~~~ Testing Connect() -> Disconnect() -> Connect() ~~~~~~~", function() { - it("#1 - Should make a connection with websocket and cache", async function() { - this.timeout(connectTime); + 'eyJqa3UiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbS9jZXJ0cyIsImtpZCI6ImtqY1NjamMzMmR3SlhYTEpEczNyMTI0c2ExIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJyZWRpcmVjdF91cmlzIjpbImh0dHA6Ly92aXAzLmVjbi5wdXJkdWUuZWR1OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiLCJodHRwOi8vbG9jYWxob3N0OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2QiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1iZWFyZXIiLCJncmFudF90eXBlcyI6WyJpbXBsaWNpdCJdLCJyZXNwb25zZV90eXBlcyI6WyJ0b2tlbiIsImlkX3Rva2VuIiwiaWRfdG9rZW4gdG9rZW4iXSwiY2xpZW50X25hbWUiOiJPcGVuQVRLIiwiY2xpZW50X3VyaSI6Imh0dHBzOi8vdmlwMy5lY24ucHVyZHVlLmVkdSIsImNvbnRhY3RzIjpbIlNhbSBOb2VsIDxzYW5vZWxAcHVyZHVlLmVkdT4iXSwic29mdHdhcmVfaWQiOiIxZjc4NDc3Zi0zNTQxLTQxM2ItOTdiNi04NjQ0YjRhZjViYjgiLCJyZWdpc3RyYXRpb25fcHJvdmlkZXIiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbSIsImlhdCI6MTUxMjAwNjc2MX0.AJSjNlWX8UKfVh-h1ebCe0MEGqKzArNJ6x0nmta0oFMcWMyR6Cn2saR-oHvU8WrtUMEr-w020mAjvhfYav4EdT3GOGtaFgnbVkIs73iIMtr8Z-Y6mDEzqRzNzVRMLghj7CyWRCNJEk0jwWjOuC8FH4UsfHmtw3ouMFomjwsNLY0', + scope: 'oada.yield:all' + } +} + +describe('~~~~~~ Testing Connect() -> Disconnect() -> Connect() ~~~~~~~', function () { + it('#1 - Should make a connection with websocket and cache', async function () { + this.timeout(connectTime) + connections[0] = await oada.connect({ domain, - token: "def", - options: connectionParameters.options, - }); + token: 'def', + options: connectionParameters.options + }) expect(connections[0]).to.contain.all.keys([ - "token", - "disconnect", - "reconnect", - "get", - "put", - "post", - "delete", - "resetCache", - "cache", - "websocket", - "_getMemoryCache", - ]); - expect(connections[0].cache).to.be.an("object"); - expect(connections[0].websocket).to.be.an("object"); + 'token', + 'disconnect', + 'reconnect', + 'get', + 'put', + 'post', + 'delete', + 'resetCache', + 'cache', + 'websocket', + '_getMemoryCache' + ]) + expect(connections[0].cache).to.be.an('object') + expect(connections[0].websocket).to.be.an('object') expect(connections[0].get).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].put).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].post).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].delete).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].resetCache).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].disconnect).to.satisfy(member => { - return typeof member === "function"; - }); - }); - - describe(`Disconnecting connection 0`, function() { - it("Should disconnect connection with websocket and cache", async () => { - connections[0].disconnect(); - }); - }); - -/* - it("#2 - Should make a connection with websocket and cache", async function() { - this.timeout(connectTime); + return typeof member === 'function' + }) + }) + + describe(`Disconnecting connection 0`, function () { + it('Should disconnect connection with websocket and cache', async () => { + connections[0].disconnect() + }) + }) + + xit('#2 - Should make a connection with websocket and cache', async function () { + this.timeout(connectTime) connections[0] = await oada.connect({ domain, - token: "def", - options: connectionParameters.options, - }); + token: 'def', + options: connectionParameters.options + }) expect(connections[0]).to.contain.all.keys([ - "token", - "disconnect", - "reconnect", - "get", - "put", - "post", - "delete", - "resetCache", - "cache", - "websocket", - "_getMemoryCache", - ]); - expect(connections[0].cache).to.be.an("object"); - expect(connections[0].websocket).to.be.an("object"); + 'token', + 'disconnect', + 'reconnect', + 'get', + 'put', + 'post', + 'delete', + 'resetCache', + 'cache', + 'websocket', + '_getMemoryCache' + ]) + expect(connections[0].cache).to.be.an('object') + expect(connections[0].websocket).to.be.an('object') expect(connections[0].get).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].put).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].post).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].delete).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].resetCache).to.satisfy(member => { - return typeof member === "function"; - }); + return typeof member === 'function' + }) expect(connections[0].disconnect).to.satisfy(member => { - return typeof member === "function"; - }); - }); -}); - -describe("~~~~connect() -> disconnect() -> connect() -> puts ~~~~~~~", () => { - var connections; - var conn; + return typeof member === 'function' + }) + }) +}) + +xdescribe('~~~~connect() -> disconnect() -> connect() -> puts ~~~~~~~', () => { + var conn before( - "First, make the connection. Cache + websockets enabled.", - async function() { + 'First, make the connection. Cache + websockets enabled.', + async function () { connection = await oada.connect({ domain, - token: "def", - }); - conn = connection; + token: 'def' + }) + conn = connection } - ); + ) - before("Disconnecting - First time", function() { - conn.disconnect(); - }); + before('Disconnecting - First time', function () { + conn.disconnect() + }) before( - "Second, make the connection. Cache + websockets enabled.", - async function() { + 'Second, make the connection. Cache + websockets enabled.', + async function () { connection = await oada.connect({ domain, - token: "def", - }); - conn = connection; + token: 'def' + }) + conn = connection } - ); + ) - it(`Should error when neither 'url' nor 'path' are supplied`, async function() { + it(`Should error when neither 'url' nor 'path' are supplied`, async function () { try { - var response = await conn.put({ + await conn.put({ data: `"123"`, tree, - type: "application/json", - }); + type: 'application/json' + }) } catch (error) { - expect(error.message).to.equal("Either path or url must be specified."); + expect(error.message).to.equal('Either path or url must be specified.') } - }); + }) - it(`Shouldn't error when 'data' contains a _type key.`, async function() { + it(`Shouldn't error when 'data' contains a _type key.`, async function () { try { var response = await conn.put({ - path: "/bookmarks/testA/sometest", - data: { _type: "application/json" }, - }); - expect(response.status).to.equal(status.NO_CONTENT); + path: '/bookmarks/testA/sometest', + data: { _type: 'application/json' } + }) + expect(response.status).to.equal(status.NO_CONTENT) } catch (error) { - console.log("data _type", error); + console.log('data _type', error) } - }); + }) - it(`Shouldn't error when 'type' is specified.`, async function() { + it(`Shouldn't error when 'type' is specified.`, async function () { var response = await conn.put({ - path: "/bookmarks/testA/somethingnew", + path: '/bookmarks/testA/somethingnew', data: `"abc123"`, - type: "application/json", - }); - expect(response.status).to.equal(status.NO_CONTENT); - }); + type: 'application/json' + }) + expect(response.status).to.equal(status.NO_CONTENT) + }) - it(`Shouldn't error when 'Content-Type' header is specified.`, async function() { + it(`Shouldn't error when 'Content-Type' header is specified.`, async function () { var response = await conn.put({ - path: "/bookmarks/testA/somethingnew", + path: '/bookmarks/testA/somethingnew', data: `"abc123"`, - headers: { "Content-Type": "application/json" }, - }); - expect(response.status).to.equal(status.NO_CONTENT); - }); + headers: { 'Content-Type': 'application/json' } + }) + expect(response.status).to.equal(status.NO_CONTENT) + }) - it("Should provide expected response status and headers when no tree is supplied.", async function() { + it('Should provide expected response status and headers when no tree is supplied.', async function () { var response = await conn.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee/test/123", - type: "application/json", - data: `"some test"`, - }); - expect(response.status.toString().charAt(0)).to.equal('2'); + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee/test/123', + type: 'application/json', + data: `"some test"` + }) + expect(response.status.toString().charAt(0)).to.equal('2') expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - "location", - ]); - }); + 'content-location', + 'x-oada-rev', + 'location' + ]) + }) - it(`Should create the data PUT in the previous test.`, async function() { + it(`Should create the data PUT in the previous test.`, async function () { var response = await conn.get({ - path: "/bookmarks/test", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: aaa", async function() { + it('retrieving previous data: aaa', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: bbb", async function() { + it('retrieving previous data: bbb', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: index-one", async function() { + it('retrieving previous data: index-one', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb/index-one' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: ccc", async function() { + it('retrieving previous data: ccc', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb/index-one/ccc' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: index-two", async function() { + it('retrieving previous data: index-two', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc/index-two", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: ddd", async function() { + it('retrieving previous data: ddd', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: test", async function() { + it('retrieving previous data: test', async function () { var response = await conn.get({ - path: "/bookmarks/test", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: aaa", async function() { + it('retrieving previous data: aaa', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: bbb", async function() { + it('retrieving previous data: bbb', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: index-one", async function() { + it('retrieving previous data: index-one', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - }); + path: '/bookmarks/test/aaa/bbb/index-one' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - it("retrieving previous data: ccc", async function() { + it('retrieving previous data: ccc', async function () { var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one/ccc", - }); - expect(response.status.toString().charAt(0)).to.equal('2'); - expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); - });*/ + path: '/bookmarks/test/aaa/bbb/index-one/ccc' + }) + expect(response.status.toString().charAt(0)).to.equal('2') + expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) + }) - /* - it("Should set watches on previous data: /bookmarks/test", async function() { + it('Should set watches on previous data: /bookmarks/test', async function () { try { var response = await conn.get({ - path: "/bookmarks/test", + path: '/bookmarks/test', watch: payload => { - console.log("handling watch valid", payload); + console.log('handling watch valid', payload) } // handleWatch("exists") - }); - expect(response.status).to.equal(status.OK); + }) + expect(response.status).to.equal(status.OK) expect(response.headers).to.include.keys([ - "content-location", - "x-oada-rev", - ]); + 'content-location', + 'x-oada-rev' + ]) } catch (error) { - console.log("Catch error", error); + console.log('Catch error', error) } - }); + }) - it("Should fail setting watches on non existent data: yyy", async function() { + it('Should fail setting watches on non existent data: yyy', async function () { try { - var response = await conn.get({ - path: "/bookmarks/test/aaa/bbb/index-one/yyy", + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/yyy', watch: payload => { - console.log("handlinng watch invalid", payload); - }, // handleWatch("exists")//handleWatch("does not exist") + console.log('handlinng watch invalid', payload) + } // handleWatch("exists")//handleWatch("does not exist") //Possible EventEmitter memory leak detected. 11 destroyed listeners added. - }); + }) } catch (error) { // console.log("error watching non existent", error.message); // expect(error.message).to.equal("Request failed with status code 404"); } - }); + }) - it("Now clean up", async function() { - await conn.resetCache(); - await conn.delete({ path: "/bookmarks/test", tree }); - }); -}); + after('Clean up', async function () { + await conn.resetCache() + await conn.delete({ path: '/bookmarks/test', tree }) + }) +}) -describe("~~~~connect() -> disconnect() -> connect() -> disconnect() -> puts ~~~~~~~", () => { - var connections; - var conn; +xdescribe('~~~~connect() -> disconnect() -> connect() -> disconnect() -> puts ~~~~~~~', () => { + var conn before( - "First, make the connection. Cache + websockets enabled.", - async function() { + 'First, make the connection. Cache + websockets enabled.', + async function () { connection = await oada.connect({ domain, - token: "def", - }); - conn = connection; + token: 'def' + }) + conn = connection } - ); + ) - before("Disconnecting - First time", function() { - conn.disconnect(); - }); + before('Disconnecting - First time', function () { + conn.disconnect() + }) before( - "Second, make the connection. Cache + websockets enabled.", - async function() { + 'Second, make the connection. Cache + websockets enabled.', + async function () { connection = await oada.connect({ domain, - token: "def", - }); - conn = connection; + token: 'def' + }) + conn = connection } - ); + ) - before("Disconnecting - Second time", function() { - conn.disconnect(); - }); + before('Disconnecting - Second time', function () { + conn.disconnect() + }) - it(`Should error when there is no active connection and neither 'url' nor 'path' are supplied`, async function() { + it(`Should error when there is no active connection and neither 'url' nor 'path' are supplied`, async function () { try { - var response = await conn.put({ + await conn.put({ data: `"123"`, tree, - type: "application/json", - }); + type: 'application/json' + }) + } catch (error) { + expect(error.message).to.equal('Either path or url must be specified.') + } + }) + + it(`Should error when there is no active connection and 'data' contains a _type key.`, async function () { + try { + await conn.put({ + path: '/bookmarks/testA/sometest', + data: { _type: 'application/json' } + }) } catch (error) { - expect(error.message).to.equal("Either path or url must be specified."); + expect(error.message).to.include('WebSocket is not open') } - }); - - // it(`Should error when there is no active connection and 'data' contains a _type key.`, async function() { - // try { - // var response = await conn.put({ - // path: "/bookmarks/testA/sometest", - // data: { _type: "application/json" } - // }); - // } catch (error) { - // expect(error.message).to.include("WebSocket is not open"); - // } - // }); - - // it(`Should error when there is no active connection and 'type' is specified.`, async function() { - // try { - // var reposnse = await conn.put({ - // path: "/bookmarks/testA/somethingnew", - // data: `"abc123"`, - // type: "application/json" - // }); - // } catch (error) { - // expect(error.message).to.equal( - // "WebSocket is not open: readyState 3 (CLOSED)" - // ); - // } - // }); - - // it(`Should error when there is no active connection and 'Content-Type' header is specified.`, async function() { - // try { - // var response = await conn.put({ - // path: "/bookmarks/testA/somethingnew", - // data: `"abc123"`, - // headers: { "Content-Type": "application/json" } - // }); - // } catch (error) { - // expect(error.message).to.equal( - // "WebSocket is not open: readyState 3 (CLOSED)" - // ); - // } - // }); - - // it("Should not retrieve any data: aaa", async function() { - // try { - // var response = await conn.get({ - // path: "/bookmarks/test/aaa" - // }); - // } catch (error) { - // expect(error.message).to.equal( - // "WebSocket is not open: readyState 3 (CLOSED)" - // ); - // } - // }); - - // it("Should not retrieve any data: bbb", async function() { - // try { - // var response = await conn.get({ - // path: "/bookmarks/test/aaa/bbb" - // }); - // } catch (error) { - // expect(error.message).to.equal( - // "WebSocket is not open: readyState 3 (CLOSED)" - // ); - // } - // }); - //}); - */ -}); - -// /* ----------------------------------------------------------------------------------- */ -// it("Should not set any watches on previous data: ccc when disconnected", async function() { -// try { -// var response = await conn.get({ -// path: "/bookmarks/test/aaa/bbb/index-one/ccc", -// watch: handleWath -// }); -// } catch (error) { -// //expect(error.message).to.include("WebSocket is not open"); -// expect(error.message).to.include("Cannot read property"); //ok, but error is not related to the websocket -// } -// }); -// }); - -// /* ----------------------------------------------------------------------------------- */ -// it("Should fail setting watches on non existent data: yyy and disconnected", async function() { -// try { -// var response = await conn.get({ -// path: "/bookmarks/test/aaa/bbb/index-one/yyy", -// watch: handleWath -// }); -// } catch (error) { -// expect(error.message).to.include("WebSocket is not open"); -// } -// }); - -// it("Now clean up", () => { -// conn.resetCache(); -// await conn.delete({path: '/bookmarks/test', tree}) -// }); -// }); + }) + + it(`Should error when there is no active connection and 'type' is specified.`, async function () { + try { + await conn.put({ + path: '/bookmarks/testA/somethingnew', + data: `"abc123"`, + type: 'application/json' + }) + } catch (error) { + expect(error.message).to.equal( + 'WebSocket is not open: readyState 3 (CLOSED)' + ) + } + }) + + it(`Should error when there is no active connection and 'Content-Type' header is specified.`, async function () { + try { + await conn.put({ + path: '/bookmarks/testA/somethingnew', + data: `"abc123"`, + headers: { 'Content-Type': 'application/json' } + }) + } catch (error) { + expect(error.message).to.equal( + 'WebSocket is not open: readyState 3 (CLOSED)' + ) + } + }) + + it('Should not retrieve any data: aaa', async function () { + try { + await conn.get({ + path: '/bookmarks/test/aaa' + }) + } catch (error) { + expect(error.message).to.equal( + 'WebSocket is not open: readyState 3 (CLOSED)' + ) + } + }) + + it('Should not retrieve any data: bbb', async function () { + try { + await conn.get({ + path: '/bookmarks/test/aaa/bbb' + }) + } catch (error) { + expect(error.message).to.equal( + 'WebSocket is not open: readyState 3 (CLOSED)' + ) + } + }) + + /* ----------------------------------------------------------------------------------- */ + it('Should not set any watches on previous data: ccc when disconnected', async function () { + try { + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/ccc', + watch: handleWath + }) + } catch (error) { + //expect(error.message).to.include("WebSocket is not open"); + expect(error.message).to.include('Cannot read property') //ok, but error is not related to the websocket + } + }) + + /* ----------------------------------------------------------------------------------- */ + it('Should fail setting watches on non existent data: yyy and disconnected', async function () { + try { + await conn.get({ + path: '/bookmarks/test/aaa/bbb/index-one/yyy', + watch: handleWath + }) + } catch (error) { + expect(error.message).to.include('WebSocket is not open') + } + }) + + after('Clean up', async () => { + conn.resetCache() + await conn.delete({ path: '/bookmarks/test', tree }) + }) +}) diff --git a/test/token.test.js b/test/token.test.js index e03f12b..f411392 100644 --- a/test/token.test.js +++ b/test/token.test.js @@ -1,132 +1,131 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const oada = require("../src/index"); -const chai = require("chai"); -var expect = chai.expect; -const status = require("http-status"); -const { domain } = require("./config"); -let connectTime = 30 * 1000; // seconds to click through oauth -let nTests = 5; -const _ = require("lodash"); -let expiredConnections = new Array(10); -oada.setDbPrefix("./test/test-data/"); +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const oada = require('../src/index') +const chai = require('chai') +const { expect } = chai +const status = require('http-status') +const { domain } = require('./config') +const connectTime = 30 * 1000 // seconds to click through oauth +const nTests = 5 +const expiredConnections = new Array(10) +oada.setDbPrefix('./test/test-data/') -let connectionParameters = { +const connectionParameters = { domain, options: { - redirect: "http://localhost:8000/oauth2/redirect.html", + redirect: 'http://localhost:8000/oauth2/redirect.html', metadata: - "eyJqa3UiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbS9jZXJ0cyIsImtpZCI6ImtqY1NjamMzMmR3SlhYTEpEczNyMTI0c2ExIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJyZWRpcmVjdF91cmlzIjpbImh0dHA6Ly92aXAzLmVjbi5wdXJkdWUuZWR1OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiLCJodHRwOi8vbG9jYWxob3N0OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2QiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1iZWFyZXIiLCJncmFudF90eXBlcyI6WyJpbXBsaWNpdCJdLCJyZXNwb25zZV90eXBlcyI6WyJ0b2tlbiIsImlkX3Rva2VuIiwiaWRfdG9rZW4gdG9rZW4iXSwiY2xpZW50X25hbWUiOiJPcGVuQVRLIiwiY2xpZW50X3VyaSI6Imh0dHBzOi8vdmlwMy5lY24ucHVyZHVlLmVkdSIsImNvbnRhY3RzIjpbIlNhbSBOb2VsIDxzYW5vZWxAcHVyZHVlLmVkdT4iXSwic29mdHdhcmVfaWQiOiIxZjc4NDc3Zi0zNTQxLTQxM2ItOTdiNi04NjQ0YjRhZjViYjgiLCJyZWdpc3RyYXRpb25fcHJvdmlkZXIiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbSIsImlhdCI6MTUxMjAwNjc2MX0.AJSjNlWX8UKfVh-h1ebCe0MEGqKzArNJ6x0nmta0oFMcWMyR6Cn2saR-oHvU8WrtUMEr-w020mAjvhfYav4EdT3GOGtaFgnbVkIs73iIMtr8Z-Y6mDEzqRzNzVRMLghj7CyWRCNJEk0jwWjOuC8FH4UsfHmtw3ouMFomjwsNLY0", - scope: "oada.yield:all", - }, -}; + 'eyJqa3UiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbS9jZXJ0cyIsImtpZCI6ImtqY1NjamMzMmR3SlhYTEpEczNyMTI0c2ExIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJyZWRpcmVjdF91cmlzIjpbImh0dHA6Ly92aXAzLmVjbi5wdXJkdWUuZWR1OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiLCJodHRwOi8vbG9jYWxob3N0OjgwMDAvb2F1dGgyL3JlZGlyZWN0Lmh0bWwiXSwidG9rZW5fZW5kcG9pbnRfYXV0aF9tZXRob2QiOiJ1cm46aWV0ZjpwYXJhbXM6b2F1dGg6Y2xpZW50LWFzc2VydGlvbi10eXBlOmp3dC1iZWFyZXIiLCJncmFudF90eXBlcyI6WyJpbXBsaWNpdCJdLCJyZXNwb25zZV90eXBlcyI6WyJ0b2tlbiIsImlkX3Rva2VuIiwiaWRfdG9rZW4gdG9rZW4iXSwiY2xpZW50X25hbWUiOiJPcGVuQVRLIiwiY2xpZW50X3VyaSI6Imh0dHBzOi8vdmlwMy5lY24ucHVyZHVlLmVkdSIsImNvbnRhY3RzIjpbIlNhbSBOb2VsIDxzYW5vZWxAcHVyZHVlLmVkdT4iXSwic29mdHdhcmVfaWQiOiIxZjc4NDc3Zi0zNTQxLTQxM2ItOTdiNi04NjQ0YjRhZjViYjgiLCJyZWdpc3RyYXRpb25fcHJvdmlkZXIiOiJodHRwczovL2lkZW50aXR5Lm9hZGEtZGV2LmNvbSIsImlhdCI6MTUxMjAwNjc2MX0.AJSjNlWX8UKfVh-h1ebCe0MEGqKzArNJ6x0nmta0oFMcWMyR6Cn2saR-oHvU8WrtUMEr-w020mAjvhfYav4EdT3GOGtaFgnbVkIs73iIMtr8Z-Y6mDEzqRzNzVRMLghj7CyWRCNJEk0jwWjOuC8FH4UsfHmtw3ouMFomjwsNLY0', + scope: 'oada.yield:all' + } +} -let expiredTokens = [ - "vxQyUQYUT0E5akXN_3LEoaPWtChJzeFNItEFgjA0", - "884rtVKCffFihijcMh8Y_0X5NsA1Srcx9TjJQY3f", - "GjLyKjR5HalUrZxauMdXJ38169T8ad9UdMvyjmRK", - "_-sNNae6ElBMlrewfGPjjv9JnVcUaHxQdJ-9pE4j", - "F3ZFCKjrR2cJY2FxCLgbqyMP7B6ybDuSMRabw4xB", - "PWgfksVlpIxxXF0xDUWDbiLTmB-MwiGP5FcmKirX", - "OkJpZdAijFOIw88QOqtdLd0hjy_ZsPICGAnRymaS", - "jAhUhVHhOKZ50K_B15_DrSYBBif2Noc9zTFYzY_5", - "iDuhpzZ-FxB2RkICfmO0vclG3sb2kYLuQtc_NZ9x", - "fdw3tuShJ-V-9AvAz2Af7lny_svkepl2TsrzcJX3", - "PIk8DfHGqXrPEdUxpL7sLsvBzptFSQnEHcqsssjb", - "OlJYiDGKbiw6B9epTE5NcmUmSoadseEnMycdC4io", - "q6so77ocgiLwOceTXocBpK6r4C6Gp3LG-pRmjwiF", - "2KYa_M-1UfZVNapZN3YpGMkKH623ZMH4jrQ320YT", -]; +const expiredTokens = [ + 'vxQyUQYUT0E5akXN_3LEoaPWtChJzeFNItEFgjA0', + '884rtVKCffFihijcMh8Y_0X5NsA1Srcx9TjJQY3f', + 'GjLyKjR5HalUrZxauMdXJ38169T8ad9UdMvyjmRK', + '_-sNNae6ElBMlrewfGPjjv9JnVcUaHxQdJ-9pE4j', + 'F3ZFCKjrR2cJY2FxCLgbqyMP7B6ybDuSMRabw4xB', + 'PWgfksVlpIxxXF0xDUWDbiLTmB-MwiGP5FcmKirX', + 'OkJpZdAijFOIw88QOqtdLd0hjy_ZsPICGAnRymaS', + 'jAhUhVHhOKZ50K_B15_DrSYBBif2Noc9zTFYzY_5', + 'iDuhpzZ-FxB2RkICfmO0vclG3sb2kYLuQtc_NZ9x', + 'fdw3tuShJ-V-9AvAz2Af7lny_svkepl2TsrzcJX3', + 'PIk8DfHGqXrPEdUxpL7sLsvBzptFSQnEHcqsssjb', + 'OlJYiDGKbiw6B9epTE5NcmUmSoadseEnMycdC4io', + 'q6so77ocgiLwOceTXocBpK6r4C6Gp3LG-pRmjwiF', + '2KYa_M-1UfZVNapZN3YpGMkKH623ZMH4jrQ320YT' +] -// describe("~~~~Testing with expired tokens -> PUTs ~~~~~~~", async function() { -// this.timeout(connectTime); -// for (let i = 0; i < nTests; i++) { -// before( -// "Make the connection with a valid but expired token. Cache + websockets enabled.", -// async function() { -// expiredConnections[i] = await oada.connect({ -// domain, -// options: connectionParameters.options, -// token: expiredTokens[i], -// }); -// }, -// ); -// } //for +xdescribe('~~~~Testing with expired tokens -> PUTs ~~~~~~~', async function () { + this.timeout(connectTime) + for (let i = 0; i < nTests; i++) { + before( + 'Make the connection with a valid but expired token. Cache + websockets enabled.', + async function () { + expiredConnections[i] = await oada.connect({ + domain, + options: connectionParameters.options, + token: expiredTokens[i] + }) + } + ) + } //for -// for (let i = 0; i < nTests; i++) { -// it(`Token [${i}]. Should create/put a resource after renewing the token.`, async function() { -// this.timeout(connectTime); -// var response = await expiredConnections[i].put({ -// path: `/bookmarks/test/test${i}/sometest`, -// data: { _type: "application/json" }, -// }); -// expect(response.status).to.equal(status.NO_CONTENT); -// }); -// } //for + for (let i = 0; i < nTests; i++) { + it(`Token [${i}]. Should create/put a resource after renewing the token.`, async function () { + this.timeout(connectTime) + var response = await expiredConnections[i].put({ + path: `/bookmarks/test/test${i}/sometest`, + data: { _type: 'application/json' } + }) + expect(response.status).to.equal(status.NO_CONTENT) + }) + } //for -// for (let i = 0; i < nTests; i++) { -// it(`Token [${i}]. Should not renew token for the subsequent PUTs.`, async function() { -// this.timeout(connectTime); -// var response = await expiredConnections[i].put({ -// path: `/bookmarks/test/test${i}/secondput${i}`, -// data: { _type: "application/json" }, -// }); -// expect(response.status).to.equal(status.NO_CONTENT); -// }); -// } //for + for (let i = 0; i < nTests; i++) { + it(`Token [${i}]. Should not renew token for the subsequent PUTs.`, async function () { + this.timeout(connectTime) + var response = await expiredConnections[i].put({ + path: `/bookmarks/test/test${i}/secondput${i}`, + data: { _type: 'application/json' } + }) + expect(response.status).to.equal(status.NO_CONTENT) + }) + } //for -// for (let i = 0; i < nTests; i++) { -// it(`Token [${i}]. Should not renew token for the GETs.`, async function() { -// this.timeout(connectTime); -// var response = await expiredConnections[i].get({ -// path: `/bookmarks/test/test${i}`, -// }); -// expect(response.status).to.equal(status.OK); -// }); -// } //for + for (let i = 0; i < nTests; i++) { + it(`Token [${i}]. Should not renew token for the GETs.`, async function () { + this.timeout(connectTime) + var response = await expiredConnections[i].get({ + path: `/bookmarks/test/test${i}` + }) + expect(response.status).to.equal(status.OK) + }) + } //for -// for (let i = 0; i < nTests; i++) { -// it(`Token [${i}]. Should not renew token for the DELs.`, async function() { -// this.timeout(connectTime); -// var response = await expiredConnections[i].delete({ -// path: `/bookmarks/test/test${i}`, -// }); -// expect(response.status).to.equal(status.NO_CONTENT); -// }); -// } //for -// }); + for (let i = 0; i < nTests; i++) { + it(`Token [${i}]. Should not renew token for the DELs.`, async function () { + this.timeout(connectTime) + var response = await expiredConnections[i].delete({ + path: `/bookmarks/test/test${i}` + }) + expect(response.status).to.equal(status.NO_CONTENT) + }) + } //for +}) -// describe("~~~~Testing with expired tokens -> GETs ~~~~~~~", async function() { -// this.timeout(connectTime); -// for (let i = nTests; i < 2 * nTests; i++) { -// before( -// "Make the connection with a valid but expired token. Cache + websockets enabled.", -// async function() { -// expiredConnections[i] = await oada.connect({ -// domain, -// options: connectionParameters.options, -// token: expiredTokens[i], -// }); -// }, -// ); -// } //for +xdescribe('~~~~Testing with expired tokens -> GETs ~~~~~~~', async function () { + this.timeout(connectTime) + for (let i = nTests; i < 2 * nTests; i++) { + before( + 'Make the connection with a valid but expired token. Cache + websockets enabled.', + async function () { + expiredConnections[i] = await oada.connect({ + domain, + options: connectionParameters.options, + token: expiredTokens[i] + }) + } + ) + } //for -// for (let i = nTests; i < 2 * nTests; i++) { -// it(`Token [${i}]. Should GET a resource after renewing the token.`, async function() { -// this.timeout(connectTime); -// var response = await expiredConnections[i].get({ -// path: `/bookmarks/test`, -// }); -// expect(response.status).to.equal(status.OK); -// }); -// } //for + for (let i = nTests; i < 2 * nTests; i++) { + it(`Token [${i}]. Should GET a resource after renewing the token.`, async function () { + this.timeout(connectTime) + var response = await expiredConnections[i].get({ + path: `/bookmarks/test` + }) + expect(response.status).to.equal(status.OK) + }) + } //for -// for (let i = nTests; i < 2 * nTests; i++) { -// it(`Token [${i}]. Should not renew token for the subsequent GETs.`, async function() { -// this.timeout(connectTime); -// var response = await expiredConnections[i].get({ -// path: `/bookmarks/test`, -// }); -// expect(response.status).to.equal(status.OK); -// }); -// } //for -// }); + for (let i = nTests; i < 2 * nTests; i++) { + it(`Token [${i}]. Should not renew token for the subsequent GETs.`, async function () { + this.timeout(connectTime) + var response = await expiredConnections[i].get({ + path: `/bookmarks/test` + }) + expect(response.status).to.equal(status.OK) + }) + } //for +}) diff --git a/test/utils.js b/test/utils.js index 811b1f4..f23fab1 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,115 +1,104 @@ -const oada = require("../src/index"); -const Promise = require("bluebird"); -const axios = require("axios"); -const uuid = require("uuid"); -oada.setDbPrefix("./test/test-data/"); -var { token, domain } = require("./config"); +const oada = require('../src/index') +const axios = require('axios') +const uuid = require('uuid') +oada.setDbPrefix('./test/test-data/') +var { token, domain } = require('./config') -async function getConnections({ domain, options, token }) { - var cYesWYes = await oada.connect({ - domain, - options, - token, - cache: { name: "cYesWYes" }, - }); +const connections = [ + { cache: false, websocket: false, name: 'cNoWNo' }, + { cache: true, websocket: false, name: 'cYesWNo' }, + { cache: false, /*websocket: true,*/ name: 'cNoWYes' }, + { cache: true, /*websocket: true,*/ name: 'cYesWYes' } +] +function getConnections ({ domain, options, token }) { + return connections.map(({ cache, websocket, name }) => { + const conn = oada.connect({ + domain, + options, + token, + websocket, + name, + cache: cache && { name } + }) - var cYesWNo = await oada.connect({ - domain, - options, - token, - websocket: false, - cache: { name: "cYesWNo" }, - }); - var cNoWYes = await oada.connect({ - domain, - options, - token, - cache: false, - name: "cNoWYes", - }); + conn.cache = cache + conn.websocket = typeof websocket === 'undefined' - var cNoWNo = await oada.connect({ - domain, - options, - token, - websocket: false, - cache: false, - name: "cNoWNo", - }); - return [cNoWNo, cYesWNo, cNoWYes, cYesWYes]; + return conn + }) } -async function putResource(data, path) { - var pieces = path.split("/bookmarks")[1].split("/"); - var newPath = "/bookmarks" + pieces.splice(0, pieces.length - 1).join("/"); - var _id = "resources/" + uuid(); - var newData = {}; - newData[pieces[0]] = { _id, _rev: 0 }; +async function putResource (data, path) { + var pieces = path.split('/bookmarks')[1].split('/') + var newPath = '/bookmarks' + pieces.splice(0, pieces.length - 1).join('/') + var _id = 'resources/' + uuid() + var newData = {} + newData[pieces[0]] = { _id, _rev: 0 } var resource = await axios({ - method: "put", - url: domain + "/" + _id, + method: 'put', + url: domain + '/' + _id, headers: { - Authorization: "Bearer " + token, - "Content-Type": "application/json", + Authorization: 'Bearer ' + token, + 'Content-Type': 'application/json' }, - data, - }); + data + }) var link = await axios({ - method: "put", + method: 'put', url: domain + newPath, headers: { - Authorization: "Bearer " + token, - "Content-Type": "application/json", + Authorization: 'Bearer ' + token, + 'Content-Type': 'application/json' }, - data: newData, - }); + data: newData + }) - return { resource, link }; + return { resource, link } } var tree = { bookmarks: { - _type: "application/vnd.oada.bookmarks.1+json", + _type: 'application/vnd.oada.bookmarks.1+json', _rev: 0, test: { - _type: "application/vnd.oada.harvest.1+json", + _type: 'application/vnd.oada.harvest.1+json', _rev: 0, aaa: { - _type: "application/vnd.oada.as-harvested.1+json", + _type: 'application/vnd.oada.as-harvested.1+json', _rev: 0, bbb: { _type: - "application/vnd.oada.as-harvested.yield-moisture-dataset.1+json", + 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', _rev: 0, - "index-one": { - "*": { + 'index-one': { + '*': { _type: - "application/vnd.oada.as-harvested.yield-moisture-dataset.1+json", + 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', _rev: 0, - "index-two": { - "*": { + 'index-two': { + '*': { _type: - "application/vnd.oada.as-harvested.yield-moisture-dataset.1+json", + 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', _rev: 0, - "index-three": { - "*": { + 'index-three': { + '*': { _type: - "application/vnd.oada.as-harvested.yield-moisture-dataset.1+json", - test: {}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, -}; + 'application/vnd.oada.as-harvested.yield-moisture-dataset.1+json', + test: {} + } + } + } + } + } + } + } + } + } + } +} module.exports = { getConnections, tree, - putResource, -}; + putResource +} diff --git a/test/watch.test.js b/test/watch.test.js index ad26e59..f0e8c50 100644 --- a/test/watch.test.js +++ b/test/watch.test.js @@ -1,208 +1,223 @@ -process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; -const oada = require("../src/index"); -const Promise = require("bluebird"); -const pretty = require("prettyjson"); -const _ = require("lodash"); -const { expect } = require("chai"); -const { token, domain } = require("./config.js"); -const { tree, getConnections } = require("./utils.js"); -oada.setDbPrefix("./test/test-data/"); -var expecting = false; - -var connections; -var expects = {}; - -async function setupWatch(conn, tre, payload) { +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 +const oada = require('../src/index') +const Promise = require('bluebird') +const _ = require('lodash') +const { expect } = require('chai') +const { token, domain } = require('./config.js') +const { tree, getConnections } = require('./utils.js') +oada.setDbPrefix('./test/test-data/') + +// Only want websocket connections for WATCH +const connections = getConnections({ + domain, + token +}).filter(({ websocket }) => websocket) + +async function setupWatch (conn, tre, payload) { //watch the endpoint var getOne = await conn.get({ - path: "/bookmarks/test", + path: '/bookmarks/test', tree: tre || tree, watch: { - payload: payload || { someExtra: "payload" }, + payload: payload || { someExtra: 'payload' }, callback: pay => { //console.log("received a watch change", pay.response.change.body); - }, - }, - }); - expect(getOne.status).to.equal(200); - return { getOne }; + } + } + }) + expect(getOne.status).to.equal(200) + return { getOne } } -describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function() { - var connOne; - var connTwo; - before(`Create connection types`, async function() { - connections = await getConnections({ domain, token }); - connOne = connections[3]; - connTwo = connections[2]; - }); - - it(`1. Watches should automatically update the cache when a single resource is created (single connection)`, async function() { - this.timeout(20000); - await connOne.delete({ path: "/bookmarks/test", tree }); - await connOne.resetCache(); +describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function () { + let connOne + let connTwo + before(`Create connection types`, async function () { + ;[connTwo, connOne] = await Promise.all(connections) + }) + + beforeEach('Reset connection', async function () { + await connOne.delete({ path: '/bookmarks/test', tree }) + await connOne.resetCache() + }) + + after('Clean up', async function () { + this.timeout(6000) + await connOne.resetCache() + await connOne.delete({ path: '/bookmarks/test', tree }) + }) + + it(`1. Watches should automatically update the cache when a single resource is created (single connection)`, async function () { + this.timeout(20000) + // create the endpoint to watch before watching var putOne = await connOne.put({ - path: "/bookmarks/test", + path: '/bookmarks/test', data: {}, - tree: tree, - }) - expect(putOne.status.toString().charAt(0)).to.equal('2'); - - var result = await setupWatch(connOne); - expect(result.getOne.status.toString().charAt(0)).to.equal('2') - // Execute a deep PUT below the watched resource - var putTwo = await connTwo.put({ - path: '/bookmarks/test/aaa', - tree, - data: { testAAA: 123 }, - }) - expect(putTwo.status.toString().charAt(0)).to.equal('2') - await Promise.delay(1000) - // Retreive the data to determine that its been cached - var getTwo = await connOne.get({ - path: '/bookmarks/test', - tree, - }) - // Compare the rev of the parent at /bookmarks/test - var getOneRev = parseInt(result.getOne.headers['x-oada-rev']); - var getTwoRev = parseInt(getTwo.headers['x-oada-rev']); - - expect(getOneRev < getTwoRev).to.equal(true) - expect(getTwo.data.aaa).to.include.keys(['_id','_rev', 'testAAA']) - }) - - it(`2. Watches should automatically update the cache when a deep endpoint creates many resources (single connection)`, async function() { - this.timeout(20000); - await connOne.delete({path:'/bookmarks/test', tree}) - await connOne.resetCache(); + tree: tree + }) + expect(putOne.status.toString().charAt(0)).to.equal('2') + + var result = await setupWatch(connOne) + expect(result.getOne.status.toString().charAt(0)).to.equal('2') + // Execute a deep PUT below the watched resource + var putTwo = await connTwo.put({ + path: '/bookmarks/test/aaa', + tree, + data: { testAAA: 123 } + }) + expect(putTwo.status.toString().charAt(0)).to.equal('2') + await Promise.delay(1000) + // Retreive the data to determine that its been cached + var getTwo = await connOne.get({ + path: '/bookmarks/test', + tree + }) + // Compare the rev of the parent at /bookmarks/test + var getOneRev = parseInt(result.getOne.headers['x-oada-rev']) + var getTwoRev = parseInt(getTwo.headers['x-oada-rev']) + + expect(getOneRev).to.be.lessThan(getTwoRev) + expect(getTwo.data.aaa).to.include.keys(['_id', '_rev', 'testAAA']) + }) + + it(`2. Watches should automatically update the cache when a deep endpoint creates many resources (single connection)`, async function () { + this.timeout(20000) + // create the endpoint to watch before watching var putOne = await connOne.put({ - path: "/bookmarks/test", + path: '/bookmarks/test', data: {}, - tree: tree, - }) - expect(putOne.status.toString().charAt(0)).to.equal('2'); - var result = await setupWatch(connOne); - // Execute a deep PUT below the watched resource - var putTwo = await connOne.put({ - path: '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', - tree, - data: { testAAA: 123 }, - }) - expect(putTwo.status.toString().charAt(0)).to.equal('2') - // Retreive the data to determine that its been cached - var getTwo = await connOne.get({ - path: '/bookmarks/test', - tree, - }) - var getOneRev = parseInt(result.getOne.headers['x-oada-rev']); - var getTwoRev = parseInt(getTwo.headers['x-oada-rev']); - expect(getOneRev < getTwoRev).to.equal(true) - - var getThree = await connOne.get({ - path: '/bookmarks/test', - tree, - }) - expect(getThree.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee).to.include.keys(['_id','_rev', 'testAAA']) + tree: tree + }) + expect(putOne.status.toString().charAt(0)).to.equal('2') + var result = await setupWatch(connOne) + // Execute a deep PUT below the watched resource + var putTwo = await connOne.put({ + path: + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', + tree, + data: { testAAA: 123 } + }) + expect(putTwo.status.toString().charAt(0)).to.equal('2') + // Retreive the data to determine that its been cached + var getTwo = await connOne.get({ + path: '/bookmarks/test', + tree + }) + var getOneRev = parseInt(result.getOne.headers['x-oada-rev']) + var getTwoRev = parseInt(getTwo.headers['x-oada-rev']) + expect(getOneRev < getTwoRev).to.equal(true) + + var getThree = await connOne.get({ + path: '/bookmarks/test', + tree + }) + expect( + getThree.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee + ).to.include.keys(['_id', '_rev', 'testAAA']) }) - it(`3. Should receive the watch changes from several concurrent PUTs to the server via another connection`, async function() { - this.timeout(45000); - await connOne.delete({path:'/bookmarks/test', tree}) - await connOne.resetCache(); + it(`3. Should receive the watch changes from several concurrent PUTs to the server via another connection`, async function () { + this.timeout(45000) // If we do not include the _rev on the deepest resource endpoint, we won't receive // the change notifications on our watch. - var newTree = _.cloneDeep(tree); - newTree.bookmarks.test.aaa.bbb["index-one"]["*"]["index-two"]["*"][ - "index-three" - ]["*"]._rev = 0; + var newTree = _.cloneDeep(tree) + newTree.bookmarks.test.aaa.bbb['index-one']['*']['index-two']['*'][ + 'index-three' + ]['*']._rev = 0 // Create the endpoint to watch before watching var putOne = await connOne.put({ - path: "/bookmarks/test", - data: { foo: "bar" }, - tree: newTree, + path: '/bookmarks/test', + data: { foo: 'bar' }, + tree: newTree }) - expect(putOne.status.toString().charAt(0)).to.equal('2'); + expect(putOne.status.toString().charAt(0)).to.equal('2') - await Promise.delay(2000); + await Promise.delay(2000) // Begin watching on connection one. - var result = await setupWatch(connOne, newTree); - await Promise.delay(2000); - expect(result.getOne.status.toString().charAt(0)).to.equal('2'); + var result = await setupWatch(connOne, newTree) + await Promise.delay(2000) + expect(result.getOne.status.toString().charAt(0)).to.equal('2') // Make concurrent PUT requests over a second connection. putOne = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', tree: newTree, - data: { testOne: 123 }, - }); + data: { testOne: 123 } + }) var putTwo = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/fff/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/fff/index-three/eee', tree: newTree, - data: { testTwo: 123 }, - }); + data: { testTwo: 123 } + }) var putThree = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ggg/index-two/ddd/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ggg/index-two/ddd/index-three/eee', tree: newTree, - data: { testThree: 123 }, - }); + data: { testThree: 123 } + }) var putFour = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', tree: newTree, - data: { testFour: 123 }, - }); + data: { testFour: 123 } + }) // Wait for the set of requests to complete by joining the promises. - await Promise.join(putOne,putTwo,putThree, putFour, (One, Two, Three, Four)=> { - putOne = One; - putTwo = Two; - putThree = Three; - putFour = Four; - }) + await Promise.join( + putOne, + putTwo, + putThree, + putFour, + (One, Two, Three, Four) => { + putOne = One + putTwo = Two + putThree = Three + putFour = Four + } + ) expect(putOne.status.toString().charAt(0)).to.equal('2') expect(putTwo.status.toString().charAt(0)).to.equal('2') expect(putThree.status.toString().charAt(0)).to.equal('2') expect(putFour.status.toString().charAt(0)).to.equal('2') - // The server needs a brief moment to send down watch notifications - await Promise.delay(5000); + // The server needs a brief moment to send down watch notifications + await Promise.delay(5000) // Now fetch the data to verify results. var response = await connOne.get({ - path: "/bookmarks/test", - tree: newTree, - }); - var putOneRev = parseInt(putOne.headers["x-oada-rev"]); - var putTwoRev = parseInt(putTwo.headers["x-oada-rev"]); - var putThreeRev = parseInt(putThree.headers["x-oada-rev"]); - var putFourRev = parseInt(putFour.headers["x-oada-rev"]); - var maxRev = Math.max(putOneRev, putTwoRev, putThreeRev, putFourRev); - var minRev = Math.min(putOneRev, putTwoRev, putThreeRev, putFourRev); - - var getOneRev = parseInt(result.getOne.headers["x-oada-rev"]); - var getTwoRev = parseInt(response.headers["x-oada-rev"]); + path: '/bookmarks/test', + tree: newTree + }) + var putOneRev = parseInt(putOne.headers['x-oada-rev']) + var putTwoRev = parseInt(putTwo.headers['x-oada-rev']) + var putThreeRev = parseInt(putThree.headers['x-oada-rev']) + var putFourRev = parseInt(putFour.headers['x-oada-rev']) + var maxRev = Math.max(putOneRev, putTwoRev, putThreeRev, putFourRev) + var minRev = Math.min(putOneRev, putTwoRev, putThreeRev, putFourRev) + + var getOneRev = parseInt(result.getOne.headers['x-oada-rev']) + var getTwoRev = parseInt(response.headers['x-oada-rev']) var responsePutTwo = parseInt( - response.data.aaa.bbb["index-one"].ccc["index-two"].fff["index-three"].eee - ._rev, - ); + response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee + ._rev + ) var responsePutThree = parseInt( - response.data.aaa.bbb["index-one"].ggg["index-two"].ddd["index-three"].eee - ._rev, - ); + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee + ._rev + ) var responseDERev = parseInt( - response.data.aaa.bbb["index-one"].ccc["index-two"].ddd["index-three"].eee - ._rev, - ); + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee + ._rev + ) - expect(putTwoRev).to.equal(responsePutTwo); - expect(putThreeRev).to.equal(responsePutThree); - expect(getTwoRev).to.equal(parseInt(response.data._rev)); + expect(putTwoRev).to.equal(responsePutTwo) + expect(putThreeRev).to.equal(responsePutThree) + expect(getTwoRev).to.equal(parseInt(response.data._rev)) expect(getOneRev < getTwoRev).to.equal(true) @@ -214,118 +229,147 @@ describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function() { expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) expect(response.data).to.include.keys(['_id', '_rev', '_type', 'aaa']) expect(response.data.aaa).to.include.keys(['_id', '_rev', 'bbb', '_type']) - expect(response.data.aaa.bbb).to.include.keys(['_id', '_rev', 'index-one', '_type']) + expect(response.data.aaa.bbb).to.include.keys([ + '_id', + '_rev', + 'index-one', + '_type' + ]) expect(response.data.aaa.bbb['index-one']).to.include.keys(['ccc', 'ggg']) - expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys(['_id', '_rev', '_type', 'index-two']) - expect(response.data.aaa.bbb['index-one'].ggg).to.include.keys(['_id', '_rev', '_type', 'index-two']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two']).to.include.keys(['ddd', 'fff']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].ddd).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].fff).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testOne', 'testFour']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testTwo']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two']).to.include.keys(['ddd']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two'].ddd).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testThree']) + expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys([ + '_id', + '_rev', + '_type', + 'index-two' + ]) + expect(response.data.aaa.bbb['index-one'].ggg).to.include.keys([ + '_id', + '_rev', + '_type', + 'index-two' + ]) + expect(response.data.aaa.bbb['index-one'].ccc['index-two']).to.include.keys( + ['ddd', 'fff'] + ) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].fff + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee + ).to.include.keys(['_id', '_rev', '_type', 'testOne', 'testFour']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee + ).to.include.keys(['_id', '_rev', '_type', 'testTwo']) + expect(response.data.aaa.bbb['index-one'].ggg['index-two']).to.include.keys( + ['ddd'] + ) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee + ).to.include.keys(['_id', '_rev', '_type', 'testThree']) expect(response.cached).to.equal(true) }) - - xit(`4. Should send a change feed when "offline" changes are made before a watch is set. This change feed should bring the cache up to date.`, async function() { - this.timeout(40000); - await connOne.delete({ path: "/bookmarks/test", tree }); - await connOne.resetCache(); - - var newTree = _.cloneDeep(tree); - newTree.bookmarks.test.aaa.bbb["index-one"]["*"]["index-two"]["*"][ - "index-three" - ]["*"]._rev = 0; + + xit(`4. Should send a change feed when "offline" changes are made before a watch is set. This change feed should bring the cache up to date.`, async function () { + this.timeout(40000) + + var newTree = _.cloneDeep(tree) + newTree.bookmarks.test.aaa.bbb['index-one']['*']['index-two']['*'][ + 'index-three' + ]['*']._rev = 0 // First, get the resource into the cache var putOne = await connOne.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', tree: newTree, - data: {testOne: 123}, + data: { testOne: 123 } }) expect(putOne.status.toString().charAt(0)).to.equal('2') // Validate the cache by doing gets var getOne = await connOne.get({ - path: "/bookmarks/test", - tree: newTree, - }); + path: '/bookmarks/test', + tree: newTree + }) //Next, create several changes over a second connection var putTwo = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/fff/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/fff/index-three/eee', tree: newTree, - data: { testTwo: 123 }, - }); + data: { testTwo: 123 } + }) var putThree = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ggg/index-two/ddd/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ggg/index-two/ddd/index-three/eee', tree: newTree, - data: { testThree: 123 }, - }); + data: { testThree: 123 } + }) var putFour = connTwo.put({ path: - "/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee", + '/bookmarks/test/aaa/bbb/index-one/ccc/index-two/ddd/index-three/eee', tree: newTree, - data: { testFour: 123 }, - }); - await Promise.join(putTwo, putThree, putFour, async function( + data: { testFour: 123 } + }) + await Promise.join(putTwo, putThree, putFour, async function ( Two, Three, - Four, + Four ) { - putTwo = Two; - putThree = Three; - putFour = Four; + putTwo = Two + putThree = Three + putFour = Four }) expect(putTwo.status.toString().charAt(0)).to.equal('2') expect(putThree.status.toString().charAt(0)).to.equal('2') expect(putFour.status.toString().charAt(0)).to.equal('2') await Promise.delay(5000) // Now, setup the watch and wait for the "offline" changes to get pushed - var result = await setupWatch(connOne, newTree); - await Promise.delay(2000); + var result = await setupWatch(connOne, newTree) + await Promise.delay(2000) // Wait out the watch notifications // Now retrieve the data tree to verify results var response = await connOne.get({ - path: "/bookmarks/test", - tree: newTree, - }); - - var putOneRev = parseInt(putOne.headers["x-oada-rev"]); - var putFourRev = parseInt(putFour.headers["x-oada-rev"]); - var putTwoRev = parseInt(putTwo.headers["x-oada-rev"]); - var putThreeRev = parseInt(putThree.headers["x-oada-rev"]); - var maxRev = Math.max(putOneRev, putTwoRev, putThreeRev, putFourRev); - var minRev = Math.min(putOneRev, putTwoRev, putThreeRev, putFourRev); - var maxRev = Math.max(putOneRev, putFourRev); - var minRev = Math.min(putOneRev, putFourRev); - - var getOneRev = parseInt(result.getOne.headers["x-oada-rev"]); - var getTwoRev = parseInt(response.headers["x-oada-rev"]); + path: '/bookmarks/test', + tree: newTree + }) + + var putOneRev = parseInt(putOne.headers['x-oada-rev']) + var putFourRev = parseInt(putFour.headers['x-oada-rev']) + var putTwoRev = parseInt(putTwo.headers['x-oada-rev']) + var putThreeRev = parseInt(putThree.headers['x-oada-rev']) + var maxRev = Math.max(putOneRev, putTwoRev, putThreeRev, putFourRev) + var minRev = Math.min(putOneRev, putTwoRev, putThreeRev, putFourRev) + var maxRev = Math.max(putOneRev, putFourRev) + var minRev = Math.min(putOneRev, putFourRev) + + var getOneRev = parseInt(result.getOne.headers['x-oada-rev']) + var getTwoRev = parseInt(response.headers['x-oada-rev']) var responsePutTwo = parseInt( - response.data.aaa.bbb["index-one"].ccc["index-two"].fff["index-three"].eee - ._rev, - ); + response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee + ._rev + ) var responsePutThree = parseInt( - response.data.aaa.bbb["index-one"].ggg["index-two"].ddd["index-three"].eee - ._rev, - ); + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee + ._rev + ) var responsePutFour = parseInt( - response.data.aaa.bbb["index-one"].ccc["index-two"].ddd["index-three"].eee - ._rev, - ); + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee + ._rev + ) var responseDERev = parseInt( - response.data.aaa.bbb["index-one"].ccc["index-two"].ddd["index-three"].eee - ._rev, - ); - var maxDERev = putFourRev; - - expect(putTwoRev).to.equal(responsePutTwo); - expect(putThreeRev).to.equal(responsePutThree); - expect(responseDERev).to.equal(maxDERev); - expect(getTwoRev).to.equal(parseInt(response.data._rev)); + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee + ._rev + ) + var maxDERev = putFourRev + + expect(putTwoRev).to.equal(responsePutTwo) + expect(putThreeRev).to.equal(responsePutThree) + expect(responseDERev).to.equal(maxDERev) + expect(getTwoRev).to.equal(parseInt(response.data._rev)) expect(getTwoRev > maxRev).to.equal(true) expect(getOneRev < minRev).to.equal(true) @@ -334,180 +378,209 @@ describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function() { expect(response.headers).to.include.keys(['content-location', 'x-oada-rev']) expect(response.data).to.include.keys(['_id', '_rev', '_type', 'aaa']) expect(response.data.aaa).to.include.keys(['_id', '_rev', 'bbb', '_type']) - expect(response.data.aaa.bbb).to.include.keys(['_id', '_rev', 'index-one', '_type']) + expect(response.data.aaa.bbb).to.include.keys([ + '_id', + '_rev', + 'index-one', + '_type' + ]) expect(response.data.aaa.bbb['index-one']).to.include.keys(['ccc', 'ggg']) - expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys(['_id', '_rev', '_type', 'index-two']) - expect(response.data.aaa.bbb['index-one'].ggg).to.include.keys(['_id', '_rev', '_type', 'index-two']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two']).to.include.keys(['ddd', 'fff']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].ddd).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].fff).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testOne', 'testFour']) - expect(response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testTwo']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two']).to.include.keys(['ddd']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two'].ddd).to.include.keys(['_id', '_rev', '_type', 'index-three']) - expect(response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee).to.include.keys(['_id', '_rev', '_type', 'testThree']) + expect(response.data.aaa.bbb['index-one'].ccc).to.include.keys([ + '_id', + '_rev', + '_type', + 'index-two' + ]) + expect(response.data.aaa.bbb['index-one'].ggg).to.include.keys([ + '_id', + '_rev', + '_type', + 'index-two' + ]) + expect(response.data.aaa.bbb['index-one'].ccc['index-two']).to.include.keys( + ['ddd', 'fff'] + ) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].fff + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].ddd['index-three'].eee + ).to.include.keys(['_id', '_rev', '_type', 'testOne', 'testFour']) + expect( + response.data.aaa.bbb['index-one'].ccc['index-two'].fff['index-three'].eee + ).to.include.keys(['_id', '_rev', '_type', 'testTwo']) + expect(response.data.aaa.bbb['index-one'].ggg['index-two']).to.include.keys( + ['ddd'] + ) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd + ).to.include.keys(['_id', '_rev', '_type', 'index-three']) + expect( + response.data.aaa.bbb['index-one'].ggg['index-two'].ddd['index-three'].eee + ).to.include.keys(['_id', '_rev', '_type', 'testThree']) expect(response.cached).to.equal(true) - - await connOne.delete({path:'/bookmarks/test', tree}) - await connOne.resetCache(); }) - it(`5. Should receive watches from 10 independent connections`, async function() { - this.timeout(50000); + it(`5. Should receive watches from 10 independent connections`, async function () { + this.timeout(50000) - var connection = await oada.connect({ - domain, - token, - }) + var connection = await oada.connect({ + domain, + token + }) - await connection.put({ - path: '/bookmarks/test', - data: {sometest: i}, - tree, - }) - - var response = await connection.get({ - path: '/bookmarks/test', - watch: { - payload: {someExtra: 'payload'}, - } - }) - expect(response.status.toString().charAt(0)).to.equal('2') - - // Create 10 connections - var testConnections = []; - for (var i = 0; i < 10; i++) { - testConnections.push(await oada.connect({ - domain, - token, - cache: {name: 'connection'+i.toString()} - })) - } - - await Promise.map(testConnections, async function(conn, i) { - for (var j = 0; j < 25; j++) { - conn.put({ - path: '/bookmarks/test/conn'+i, - type: 'application/json', + await connection.put({ + path: '/bookmarks/test', + data: { sometest: i }, + tree + }) + + var response = await connection.get({ + path: '/bookmarks/test', + watch: { + payload: { someExtra: 'payload' } + } + }) + expect(response.status.toString().charAt(0)).to.equal('2') + + // Create 10 connections + var testConnections = [] + for (var i = 0; i < 10; i++) { + testConnections.push( + await oada.connect({ + domain, + token, + cache: { name: 'connection' + i.toString() } + }) + ) + } + + await Promise.map(testConnections, async function (conn, i) { + for (var j = 0; j < 25; j++) { + conn.put({ + path: '/bookmarks/test/conn' + i, + type: 'application/json', tree, - data: { [`put${j}`]: `value${j}`}, - }) - } - }) - - await Promise.delay(35000); - - var getOne = await connection.get({ - path: '/bookmarks/test', - }) -// expect(getOne.cached).to.equal(true) - for (var i = 0; i < 10; i++) { + data: { [`put${j}`]: `value${j}` } + }) + } + }) + + await Promise.delay(35000) + + var getOne = await connection.get({ + path: '/bookmarks/test' + }) + // expect(getOne.cached).to.equal(true) + for (var i = 0; i < 10; i++) { for (var k = 0; k < 25; k++) { - expect(getOne.data['conn'+i]).to.include.key('put'+k) - } - } - // Now wipe out all of the caches - await Promise.map(testConnections, async function(conn, i) { - await conn.resetCache(); - }) + expect(getOne.data['conn' + i]).to.include.key('put' + k) + } + } + // Now wipe out all of the caches + await Promise.map(testConnections, async function (conn) { + await conn.resetCache() + }) await connection.put({ - path: "/bookmarks/test", + path: '/bookmarks/test', data: { sometest: i }, tree - }); + }) - var watch_count = 0; + var watch_count = 0 var response = await connection.get({ - path: "/bookmarks/test", + path: '/bookmarks/test', watch: { - payload: { someExtra: "payload" }, - callback: pay => { - watch_count++; - }, - }, - }); - expect(response.status).to.equal(200); + payload: { someExtra: 'payload' }, + callback: () => { + watch_count++ + } + } + }) + expect(response.status).to.equal(200) // Create 10 connections - var testConnections = []; + var testConnections = [] for (var i = 0; i < 10; i++) { testConnections.push( await oada.connect({ domain, token, - cache: { name: "connection" + i.toString() }, - }), - ); + cache: { name: 'connection' + i.toString() } + }) + ) } - await Promise.map(testConnections, async function(conn, i) { + await Promise.map(testConnections, async function (conn, i) { await conn.put({ - path: "/bookmarks/test", - data: { [`conn`+i]: { 'put': 'value' }}, + path: '/bookmarks/test', + data: { [`conn` + i]: { put: 'value' } }, tree - }); - }); + }) + }) - await Promise.delay(2000); + await Promise.delay(2000) var getOne = await connection.get({ - path: "/bookmarks/test", - }); - expect(watch_count).to.equal(10); + path: '/bookmarks/test' + }) + expect(watch_count).to.equal(10) for (let i = 0; i < 10; i++) { - expect(getOne.data["conn" + i]).to.include.key("put"); + expect(getOne.data['conn' + i]).to.include.key('put') } // Now wipe out all of the caches - await Promise.map(testConnections, async function(conn, i) { - await conn.resetCache(); - }); - }); - - it(`6. Should not send a change feed when the rev difference due to "offline" changes is greater than 10. Instead, the whole resource should simply be sent.`, async function() { - this.timeout(25000); - await connOne.delete({ path: "/bookmarks/test", tree }); - await connOne.resetCache(); - var newTree = _.cloneDeep(tree); + await Promise.map(testConnections, async function (conn) { + await conn.resetCache() + }) + }) + + it(`6. Should not send a change feed when the rev difference due to "offline" changes is greater than 10. Instead, the whole resource should simply be sent.`, async function () { + this.timeout(25000) + + var newTree = _.cloneDeep(tree) // First, get the resource into the cache var putOne = await connOne.put({ - path: "/bookmarks/test/aaa", + path: '/bookmarks/test/aaa', tree: newTree, - data: {testOne: 123}, + data: { testOne: 123 } }) expect(putOne.status.toString().charAt(0)).to.equal('2') // Validate the cache by doing gets - var getOne = await connOne.get({ - path: "/bookmarks/test", - tree: newTree, - }); + await connOne.get({ + path: '/bookmarks/test', + tree: newTree + }) //Next, create several changes over a second connection for (var j = 0; j < 11; j++) { await connTwo.put({ - path: "/bookmarks/test/aaa", - type: "application/json", - data: { [`put${j}`]: `value${j}` }, - }); + path: '/bookmarks/test/aaa', + type: 'application/json', + data: { [`put${j}`]: `value${j}` } + }) } - await Promise.delay(10000); + await Promise.delay(10000) - var getT = await connTwo.get({ - path: "/bookmarks/test", - }); + await connTwo.get({ + path: '/bookmarks/test' + }) var getTwo = await connTwo.get({ - path: "/bookmarks/test/aaa", - }); + path: '/bookmarks/test/aaa' + }) // Make sure the puts made it to the server for (var j = 0; j < 11; j++) { - expect(getTwo.data).to.include.key("put" + j); + expect(getTwo.data).to.include.key('put' + j) } // Now, setup the watch and wait for the "offline" changes to get pushed - var result = await setupWatch(connOne, newTree); - await Promise.delay(5000); + await setupWatch(connOne, newTree) + await Promise.delay(5000) // Wait out the watch notifications // Now retrieve the data tree to verify results var response = await connOne.get({ @@ -520,70 +593,66 @@ describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function() { expect(response.cached).to.equal(true) expect(response).to.include.keys(['data']) expect(response.data) - for (var j = 0; j < 11; j++) { - expect(response.data['aaa']).to.include.key('put'+j) - } - await connOne.delete({path:'/bookmarks/test', tree}) - await connOne.resetCache(); - }); - - xit(`7. The tree is needed to decide what documents to sync given a change document where a link connected to a large, preexisting tree`, async function() { - this.timeout(15000); + for (var j = 0; j < 11; j++) { + expect(response.data['aaa']).to.include.key('put' + j) + } + }) + + xit(`7. The tree is needed to decide what documents to sync given a change document where a link connected to a large, preexisting tree`, async function () { + this.timeout(15000) var newTree = { bookmarks: { aaa: _.cloneDeep(tree.bookmarks.test.aaa), - _type: "application/vnd.oada.bookmarks.1+json", - _rev: 0, - }, - }; + _type: 'application/vnd.oada.bookmarks.1+json', + _rev: 0 + } + } // Add some extra subtree so we can create it quickly - newTree.bookmarks.aaa.ddd = _.cloneDeep(tree.bookmarks.test.aaa.bbb); + newTree.bookmarks.aaa.ddd = _.cloneDeep(tree.bookmarks.test.aaa.bbb) - await connOne.delete({path:'/bookmarks/aaa', tree:newTree}) - await connOne.delete({path:'/bookmarks/test', tree}) - await connOne.resetCache(); + await connOne.delete({ path: '/bookmarks/aaa', tree: newTree }) // Create a tree of data that isn't cached locally - var putOne = await connTwo.put({ + var putOne = await connTwo.put({ path: '/bookmarks/aaa/bbb/index-one/ccc', - data: {putOne: 'bar'}, + data: { putOne: 'bar' }, tree: newTree }) - expect(putOne.status.toString().charAt(0)).to.equal('2'); + expect(putOne.status.toString().charAt(0)).to.equal('2') // Put to some other path that isn't in the original tree. This path should // be omitted when it is linked to the other tree var putTwo = await connTwo.put({ path: '/bookmarks/aaa/ddd/index-one/ccc/index-two/ddd', - data: {putTwo: 'foo'}, + data: { putTwo: 'foo' }, tree: newTree }) - expect(putTwo.status.toString().charAt(0)).to.equal('2'); + expect(putTwo.status.toString().charAt(0)).to.equal('2') // Create the bookmarks/test endpoint we're going to watch var putThree = await connOne.put({ path: '/bookmarks/test', - data: {putThree: 'foo'}, + data: { putThree: 'foo' }, tree }) - expect(putThree.status.toString().charAt(0)).to.equal('2'); + expect(putThree.status.toString().charAt(0)).to.equal('2') // Setup the watch on bookmarks/test - var result = await setupWatch(connOne, tree); - await Promise.delay(5000); + await setupWatch(connOne, tree) + await Promise.delay(5000) var getOne = await connOne.get({ - path: '/bookmarks/aaa', + path: '/bookmarks/aaa' }) - expect(getOne.status.toString().charAt(0)).to.equal('2'); + expect(getOne.status.toString().charAt(0)).to.equal('2') // Now link to the pre-existing tree and watch the changes come in. var putFour = await connTwo.put({ path: '/bookmarks/test', - data: {aaa: {_id: getOne.data._id, _rev: getOne.data._rev}}, + data: { aaa: { _id: getOne.data._id, _rev: getOne.data._rev } }, tree }) - expect(putFour.status.toString().charAt(0)).to.equal('2'); + expect(putFour.status.toString().charAt(0)).to.equal('2') // Wait for the changes to propagate back await Promise.delay(5000) @@ -594,10 +663,15 @@ describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function() { path: '/bookmarks/test', tree }) - expect(getTwo.status.toString().charAt(0)).to.equal('2'); + expect(getTwo.status.toString().charAt(0)).to.equal('2') // The linked data should now be present - expect(getTwo.data.aaa.bbb['index-one'].ccc).to.include.keys(['_id', '_rev', '_type', 'putOne']) + expect(getTwo.data.aaa.bbb['index-one'].ccc).to.include.keys([ + '_id', + '_rev', + '_type', + 'putOne' + ]) // Everything should've been cached expect(getTwo.cached).to.equal(true) @@ -607,50 +681,47 @@ describe(`~~~~~~~~~~~WATCH~~~~~~~~~~~~~~`, function() { expect(getTwo.data.aaa.ddd).to.have.keys(['_id', '_rev']) }) + it(`8. Messages should cease to be transmitted after calling unwatch`, async function () { + this.timeout(10000) - xit(`8. Messages should cease to be transmitted after calling unwatch`, async function() { - this.timeout(10000); + await connOne.put({ + path: `/bookmarks/test`, + tree, + data: {} + }) // Setup a counter of watch messages received. - let counter = 0; + let counter = 0 + function callback () { + counter++ + } - var getOne = await connOne.get({ + await connOne.get({ path: '/bookmarks/test', tree, watch: { - payload: {someExtra: 'payload'}, - callback: (pay) => { - counter++; - console.log('received a thing'); - } + payload: { someExtra: 'payload' }, + callback } }) - await Promise.delay(3000); + await Promise.delay(3000) // Produce a message await connOne.put({ path: `/bookmarks/test`, tree, - data: {"foo": "bar"} + data: { foo: 'bar' } }) - expect(counter).to.equal(1); + await Promise.delay(200) + const prevCount = counter - await connOne.delete({ - unwatch: true, - path: `/bookmarks/test`, - tree, - }) + await connOne.unwatch(callback) await connOne.put({ path: `/bookmarks/test`, tree, - data: {"footwo": "bar"} + data: { footwo: 'bar' } }) - expect(counter).to.equal(1); - }) - - xit('Now clean up', async function() { - this.timeout(6000); - await connOne.resetCache(); - await connOne.delete({path:'/bookmarks/test', tree}) + await Promise.delay(200) + expect(counter).to.equal(prevCount) }) -}); +}) diff --git a/yarn.lock b/yarn.lock new file mode 100755 index 0000000..83f691e --- /dev/null +++ b/yarn.lock @@ -0,0 +1,2119 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@oada/oada-certs@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@oada/oada-certs/-/oada-certs-1.0.2.tgz#fceceec712dca45fda279068a841886063d78de0" + integrity sha512-2HFwwx8UBfkDMDs1wAhTGg8t/0uDRHRUuNOT9DawsURsjFnUix6keGkxNnVXD1oToJ0wYiw8yhmbNPn6kYGIMA== + dependencies: + bluebird "^3.7.1" + debug "^4.1.1" + deep-equal "^1.1.1" + execa "^3.2.0" + inquirer "^7.0.0" + jsck "^0.3.2" + jsonwebtoken "^8.5.1" + jws "^3.2.2" + minimist "^1.2.0" + pem-jwk "^2.0.0" + skeemas "^1.2.5" + superagent "^5.1.0" + url "^0.11.0" + uuid "^3.3.3" + +"@oada/oada-certs@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@oada/oada-certs/-/oada-certs-3.1.6.tgz#c8bb1866229c9483a7b99520f861ef006c73467d" + integrity sha512-RrwxwOzFlVblATs88ui8hSLbATYJ5OIO5O3urgl+h/k2SyQLYPCKqTCgSz3YnOv78y9bK6rT+xE/fnbYJ52vlQ== + dependencies: + bluebird "^3.7.1" + debug "^4.1.1" + deep-equal "^1.1.1" + inquirer "^7.0.0" + jsck "^0.3.2" + lodash "^4.17.15" + minimist "^1.2.2" + node-jose "^1.1.3" + pem-jwk "^2.0.0" + skeemas "^1.2.5" + superagent "^5.1.0" + url "^0.11.0" + uuid "^3.3.3" + +"@oada/oada-id-client@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@oada/oada-id-client/-/oada-id-client-1.0.7.tgz#a6a878e5353edf5fc976501df9e24e5ecfeec6ba" + integrity sha512-D7fHT/yIC3R4wD/bwpsaQgOyDknshgluJoor9NlhsBm8OQnfB90PzlAXGbEg3J675r2OK+QT8ljNv993kUXOpw== + dependencies: + "@oada/oada-certs" "^3.1.6" + bluebird "^3.7.2" + grunt-parallel "^0.5.1" + is-wsl "^2.1.1" + jsonwebtoken "^8.5.1" + jws "^4.0.0" + jwt-bearer-client-auth "^1.0.0" + oauth-dyn-reg "^1.0.2" + object-assign "^4.1.1" + open "^7.0.3" + pem-jwk "^2.0.0" + superagent "^5.2.2" + urijs "^1.18.12" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +abort-controller@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +abstract-leveldown@^6.2.1, abstract-leveldown@~6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.2.tgz#677425beeb28204367c7639e264e93ea4b49971a" + integrity sha512-/a+Iwj0rn//CX0EJOasNyZJd2o8xur8Ce9C57Sznti/Ilt/cb6Qd8/k98A4ZOklXgTG+iAYYUs1OTG0s1eH+zQ== + dependencies: + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.0.3.tgz#b4b6159343c74b0c5197b2817854782d8f748c4a" + integrity sha512-jzewKKpZbaYUa6HTThnrl+GrJhzjEAeuc7hTVpZdzg7kupXZFoqQDFwyOwLNbmJKJlmzw8yiipMPkDiuKkT06Q== + dependencies: + level-concat-iterator "~2.0.0" + xtend "~4.0.0" + +afterward@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/afterward/-/afterward-2.0.0.tgz#966a7531d2fdb41bff67b4ea83abd9ebcbef5eb2" + integrity sha1-lmp1MdL9tBv/Z7Tqg6vZ68vvXrI= + dependencies: + define-error "~1.0.0" + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argsarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb" + integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs= + +asn1.js@^5.0.1: + version "5.3.0" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.3.0.tgz#439099fe9174e09cff5a54a9dda70260517e8689" + integrity sha512-WHnQJFcOrIWT1RLOkFFBQkFVvyt9BPOOrH+Dp152Zk4R993rSzXUGPmkybIcUFhHE2d/iHH+nCaOWVCDbO8fgA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + +bluebird@^3.5.1, bluebird@^3.7.1, bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-from@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + integrity sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ== + +buffer@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.5.0.tgz#9c3caa3d623c33dd1c7ef584b89b88bf9c9bc1ce" + integrity sha512-9FTEDjLjwoAkEwyMGDjYJQN2gfRgOKBKRfiglhvibGbpeeU/pQn1bJxQqm32OD/AIeEuHxU9roxXxg34Byp/Ww== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +capture-stack-trace@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" + integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== + +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== + dependencies: + check-error "^1.0.2" + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +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-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-buffer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +component-emitter@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concurrent-queue@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/concurrent-queue/-/concurrent-queue-7.0.2.tgz#3cfccfa8b9ce08ca23d85319f5c36ec1bf9540fa" + integrity sha1-PPzPqLnOCMoj2FMZ9cNuwb+VQPo= + dependencies: + afterward "~2.0.0" + define-error "~1.0.0" + eventuate "~4.0.0" + object-assign "~4.0.1" + on-error "~2.1.0" + once "~1.3.2" + promise-polyfill "~2.1.0" + +cookiejar@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^7.0.0: + 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 "^2.0.0" + which "^2.0.1" + +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-equal@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +deferred-leveldown@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.1.0.tgz#c21e40641a8e48530255a4ad31371cc7fe76b332" + integrity sha512-PvDY+BT2ONu2XVRgxHb77hYelLtMYxKSGuWuJJdVRXh9ntqx9GYTFJno/SKAz5xcd+yjQwyQeIZrUPjPvA52mg== + dependencies: + abstract-leveldown "~6.0.0" + inherits "^2.0.3" + +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + +define-error@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-error/-/define-error-1.0.0.tgz#5fb48a45dd5f65f88f82b0c9a223c0193379d3fe" + integrity sha1-X7SKRd1fZfiPgrDJoiPAGTN50/4= + dependencies: + capture-stack-trace "~1.0.0" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +double-ended-queue@2.1.0-0: + version "2.1.0-0" + resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + 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== + +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + +end-of-stream@^1.1.0: + 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" + +end-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/end-stream/-/end-stream-0.1.0.tgz#32003f3f438a2b0143168137f8fa6e9866c81ed5" + integrity sha1-MgA/P0OKKwFDFoE3+PpumGbIHtU= + dependencies: + write-stream "~0.4.3" + +errno@~0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg== + dependencies: + prr "~1.0.1" + +es-abstract@^1.17.0-next.1: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-denodeify@^0.1.1: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-denodeify/-/es6-denodeify-0.1.5.tgz#31d4d5fe9c5503e125460439310e16a2a3f39c1f" + integrity sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8= + +es6-promise@^4.2.8: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventuate@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventuate/-/eventuate-4.0.0.tgz#4e269055316fff4109078141f926600c74b571a0" + integrity sha1-TiaQVTFv/0EJB4FB+SZgDHS1caA= + dependencies: + define-error "~1.0.0" + object-assign "~3.0.0" + shallow-copy "0.0.1" + +execa@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + 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" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +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== + +fetch-cookie@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.7.3.tgz#b8d023f421dd2b2f4a0eca9cd7318a967ed4eed8" + integrity sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA== + dependencies: + es6-denodeify "^0.1.1" + tough-cookie "^2.3.3" + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +foreach@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +form-data@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" + integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formidable@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9" + integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + +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== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-stream@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" + integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== + dependencies: + pump "^3.0.0" + +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + 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" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +grunt-parallel@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/grunt-parallel/-/grunt-parallel-0.5.1.tgz#5f7a3376f6185424128c4b2a2cf2e2d07c37f5d9" + integrity sha1-X3ozdvYYVCQSjEsqLPLi0Hw39dk= + dependencies: + lpad "~0.1.0" + q "~0.8.12" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +http-status@^1.2.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/http-status/-/http-status-1.4.2.tgz#75406e829dca9bfdf92972c579b47cd6a58ab6c8" + integrity sha512-mBnIohUwRw9NyXMEMMv8/GANnzEYUj0Y8d3uL01zDWFkxUjYyZ6rgCaAI2zZ1Wb34Oqtbx/nFZolPRDc8Xlm5A== + +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== + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +immediate@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inquirer@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^3.0.0" + 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.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@~2.0.3: + 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, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-docker@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" + integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "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@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-regex@^1.0.4, is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-stream@^2.0.0: + version "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-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +is-wsl@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" + integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsck@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/jsck/-/jsck-0.3.2.tgz#8e06b31bb57b0090e503dd4ee6ad0f269dff1a55" + integrity sha1-jgazG7V7AJDlA91O5q0PJp3/GlU= + +json-pointer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.0.tgz#8e500550a6aac5464a473377da57aa6cc22828d7" + integrity sha1-jlAFUKaqxUZKRzN32leqbMIoKNc= + dependencies: + foreach "^2.0.4" + +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + +jwt-bearer-client-auth@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jwt-bearer-client-auth/-/jwt-bearer-client-auth-1.0.0.tgz#a7cfaec70fab87d6c5cb8edfd5eae56f8ffa7568" + integrity sha512-UIju0+NntJl0KpoTGJhJKkdLvDEQs877hfI9N+J1toXCnaYRjNjs52iitJwBJMps0eYkCIiJBWizV++XnWKlbg== + dependencies: + "@oada/oada-certs" "^1.0.2" + bluebird "^3.7.2" + jsonwebtoken "^8.5.1" + object-assign "^4.0.1" + pem-jwk "^2.0.0" + +level-codec@9.0.1, level-codec@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.1.tgz#042f4aa85e56d4328ace368c950811ba802b7247" + integrity sha512-ajFP0kJ+nyq4i6kptSM+mAvJKLOg1X5FiFPtLG9M5gCEZyBmgDi3FkDrvlMkEzrUn1cWxtvVmrvoS4ASyO/q+Q== + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-js@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-5.0.1.tgz#c68f8e55c3331bcba1f5b448b5cc833f21c68628" + integrity sha512-Jse0/UP2qFl2YN8HISnxwQIRHNM3vJLafkTKjw4tZs9Vi8VGUFVmGvg/WMn5To/6KHyYJTXZvUzuouoaZuPGXA== + dependencies: + abstract-leveldown "~6.2.1" + immediate "~3.2.3" + inherits "^2.0.3" + ltgt "^2.1.2" + +level-packager@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level-write-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/level-write-stream/-/level-write-stream-1.0.0.tgz#3f7fbb679a55137c0feb303dee766e12ee13c1dc" + integrity sha1-P3+7Z5pVE3wP6zA97nZuEu4Twdw= + dependencies: + end-stream "~0.1.0" + +level@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/level/-/level-6.0.0.tgz#d216fb9b9c3940bcec15c5880d8da775ca086c5c" + integrity sha512-3oAi7gXLLNr7pHj8c4vbI6lHkXf35m8qb7zWMrNTrOax6CXBVggQAwL1xnC/1CszyYrW3BsLXsY5TMgTxtKfFA== + dependencies: + level-js "^5.0.0" + level-packager "^5.1.0" + leveldown "^5.4.0" + opencollective-postinstall "^2.0.0" + +leveldown@5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.4.1.tgz#83a8fdd9bb52b1ed69be2ef59822b6cdfcdb51ec" + integrity sha512-3lMPc7eU3yj5g+qF1qlALInzIYnkySIosR1AsUKFjL9D8fYbTLuENBAeDRZXIG4qeWOAyqRItOoLu2v2avWiMA== + dependencies: + abstract-leveldown "~6.2.1" + napi-macros "~2.0.0" + node-gyp-build "~4.1.0" + +leveldown@^5.4.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" + integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== + dependencies: + abstract-leveldown "~6.2.1" + napi-macros "~2.0.0" + node-gyp-build "~4.1.0" + +levelup@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.1.0.tgz#49ab5d3a341731cd102f91c6bc17a1acb1969a17" + integrity sha512-+Qhe2/jb5affN7BeFgWUUWVdYoGXO2nFS3QLEZKZynnQyP9xqA+7wgOz3fD8SST2UKpHQuZgjyJjTcB2nMl2dQ== + dependencies: + deferred-leveldown "~5.1.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + xtend "~4.0.0" + +levelup@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.3.2.tgz#31c5b1b29f146d1d35d692e01a6da4d28fa55ebd" + integrity sha512-cRTjU4ktWo59wf13PHEiOayHC3n0dOh4i5+FHr4tv4MX9+l7mqETicNq3Aj07HKlLdk0z5muVoDL2RD+ovgiyA== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash@^4.17.10, lodash@^4.17.15: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +lpad@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lpad/-/lpad-0.1.0.tgz#e4c60c29139321c5970de493b496ae0d774cd2a7" + integrity sha1-5MYMKROTIcWXDeSTtJauDXdM0qc= + +ltgt@2.2.1, ltgt@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +methods@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +mime-db@1.43.0: + version "1.43.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" + integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== + +mime-types@^2.1.12: + version "2.1.26" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" + integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== + dependencies: + mime-db "1.43.0" + +mime@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" + integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== + +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== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.2, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" + integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== + dependencies: + minimist "^1.2.5" + +mocha-steps@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/mocha-steps/-/mocha-steps-1.3.0.tgz#2449231ec45ec56810f65502cb22e2571862957f" + integrity sha512-KZvpMJTqzLZw3mOb+EEuYi4YZS41C9iTnb7skVFRxHjUd1OYbl64tCMSmpdIRM9LnwIrSOaRfPtNpF5msgv6Eg== + +mocha@^7.0.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.1.tgz#89fbb30d09429845b1bb893a830bf5771049a441" + integrity sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.3" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + +moment@^2.22.2: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +node-fetch@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.4.1.tgz#b2e38f1117b8acbedbe0524f041fb3177188255d" + integrity sha512-P9UbpFK87NyqBZzUuDBDz4f6Yiys8xm8j7ACDbi6usvFm6KItklQUKjeoqTrYS/S1k6I8oaOC2YLLDr/gg26Mw== + +node-forge@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee" + integrity sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q== + +node-gyp-build@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" + integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== + +node-jose@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/node-jose/-/node-jose-1.1.4.tgz#af3f44a392e586d26b123b0e12dc09bef1e9863b" + integrity sha512-L31IFwL3pWWcMHxxidCY51ezqrDXMkvlT/5pLTfNw5sXmmOLJuN6ug7txzF/iuZN55cRpyOmoJrotwBQIoo5Lw== + dependencies: + base64url "^3.0.1" + browserify-zlib "^0.2.0" + buffer "^5.5.0" + es6-promise "^4.2.8" + lodash "^4.17.15" + long "^4.0.0" + node-forge "^0.8.5" + process "^0.11.10" + react-zlib-js "^1.0.4" + uuid "^3.3.3" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +oauth-dyn-reg@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/oauth-dyn-reg/-/oauth-dyn-reg-1.0.2.tgz#f57200dc6cef3a302c263c1c8b4fdfaa110bf4c8" + integrity sha512-HN9/rtCTCGRxZzdgrD8tgjYV8ghK+wcX1WSUUyzHXkfH05S1XpOs2fIHiemKQQ5/VdFra3HmIJbzazob44Iuow== + dependencies: + superagent "^5.2.1" + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-assign@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= + +object-assign@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.0.1.tgz#99504456c3598b5cad4fc59c26e8a9bb107fe0bd" + integrity sha1-mVBEVsNZi1ytT8WcJuipuxB/4L0= + +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-is@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0, object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" + integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +on-error@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/on-error/-/on-error-2.1.0.tgz#bac56e4b49804c49167150dad2759b4261117dce" + integrity sha1-usVuS0mATEkWcVDa0nWbQmERfc4= + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +once@~1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA= + dependencies: + wrappy "1" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + +open@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/open/-/open-7.0.3.tgz#db551a1af9c7ab4c7af664139930826138531c48" + integrity sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +opencollective-postinstall@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" + integrity sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + +p-limit@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" + integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + +pem-jwk@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pem-jwk/-/pem-jwk-2.0.0.tgz#1c5bb264612fc391340907f5c1de60c06d22f085" + integrity sha512-rFxu7rVoHgQ5H9YsP50dDWf0rHjreVA2z0yPiWr5WdH/UHb29hKtF7h6l8vNd1cbYR1t0QL+JKhW55a2ZV4KtA== + dependencies: + asn1.js "^5.0.1" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +pouchdb@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/pouchdb/-/pouchdb-7.2.1.tgz#619e3d5c2463ddd94a4b1bf40d44408c46e9de79" + integrity sha512-AoDPdr6tFqj3xs7oiD2oioYj5MMu87c3UemRHZ/p++BwU+ZsKn5jpnL09OvWTLvMvaICGAOufiaUzmM1/KKoKQ== + dependencies: + abort-controller "3.0.0" + argsarray "0.0.1" + buffer-from "1.1.0" + clone-buffer "1.0.0" + double-ended-queue "2.1.0-0" + fetch-cookie "0.7.3" + immediate "3.0.6" + inherits "2.0.4" + level "6.0.0" + level-codec "9.0.1" + level-write-stream "1.0.0" + leveldown "5.4.1" + levelup "4.1.0" + ltgt "2.2.1" + node-fetch "2.4.1" + readable-stream "1.0.33" + spark-md5 "3.0.0" + through2 "3.0.1" + uuid "3.3.3" + vuvuzela "1.0.3" + +prettyjson@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prettyjson/-/prettyjson-1.2.1.tgz#fcffab41d19cab4dfae5e575e64246619b12d289" + integrity sha1-/P+rQdGcq0365eV15kJGYZsS0ok= + dependencies: + colors "^1.1.2" + minimist "^1.2.0" + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +promise-polyfill@~2.1.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-2.1.4.tgz#7319223532c26ac3e555e7e9bcc0cf76201b51ad" + integrity sha1-cxkiNTLCasPlVefpvMDPdiAbUa0= + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +q@~0.8.12: + version "0.8.12" + resolved "https://registry.yarnpkg.com/q/-/q-0.8.12.tgz#9162a91e11819c4bcda7da15cf5fefaad0778823" + integrity sha1-kWKpHhGBnEvNp9oVz1/vqtB3iCM= + +qs@^6.9.1: + version "6.9.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e" + integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +react-zlib-js@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/react-zlib-js/-/react-zlib-js-1.0.4.tgz#dd2b9fbf56d5ab224fa7a99affbbedeba9aa3dc7" + integrity sha512-ynXD9DFxpE7vtGoa3ZwBtPmZrkZYw2plzHGbanUjBOSN4RtuXdektSfABykHtTiWEHMh7WdYj45LHtp228ZF1A== + +readable-stream@1.0.33: + version "1.0.33" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.33.tgz#3a360dd66c1b1d7fd4705389860eda1d0f61126c" + integrity sha1-OjYN1mwbHX/UcFOJhg7aHQ9hEmw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@2 || 3", readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~0.0.2: + version "0.0.4" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-0.0.4.tgz#f32d76e3fb863344a548d79923007173665b3b8d" + integrity sha1-8y124/uGM0SlSNeZIwBxc2ZbO40= + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "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== + +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" + +run-async@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" + integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== + dependencies: + is-promise "^2.1.0" + +rxjs@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" + integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, 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== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.6.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +shallow-copy@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + integrity sha1-QV9CcC1z2BAzApLMXuhurhoRoXA= + +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@^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== + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +skeemas-json-pointer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/skeemas-json-pointer/-/skeemas-json-pointer-1.0.0.tgz#c652feeb421f1cb7849fd88a0c0427d1539048bd" + integrity sha1-xlL+60IfHLeEn9iKDAQn0VOQSL0= + +skeemas-json-refs@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/skeemas-json-refs/-/skeemas-json-refs-1.0.1.tgz#3a8347278e20778ad499d7e439d6e44c4f565807" + integrity sha1-OoNHJ44gd4rUmdfkOdbkTE9WWAc= + dependencies: + skeemas-json-pointer "^1.0.0" + +skeemas@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/skeemas/-/skeemas-1.2.5.tgz#060c49be93828a40064cd7a32d743bdfb64f6de8" + integrity sha512-itiq9FA/8CYIUZK/jtSrzWyipJkJNgAtsVpVySuJ+laamzuk9lEdjOqgMI8gWco/UyzJkhld6QMWdZ4zFOAOIw== + dependencies: + skeemas-json-pointer "^1.0.0" + skeemas-json-refs "^1.0.1" + +spark-md5@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.0.tgz#3722227c54e2faf24b1dc6d933cc144e6f71bfef" + integrity sha1-NyIifFTi+vJLHcbZM8wUTm9xv+8= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +superagent@^5.1.0, superagent@^5.2.1, superagent@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.2.2.tgz#6ff726c5642795b2c27009e92687c8e69a6bb07d" + integrity sha512-pMWBUnIllK4ZTw7p/UaobiQPwAO5w/1NRRTDpV0FTVNmECztsxKspj3ZWEordVEaqpZtmOQJJna4yTLyC/q7PQ== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.2" + debug "^4.1.1" + fast-safe-stringify "^2.0.7" + form-data "^3.0.0" + formidable "^1.2.1" + methods "^1.1.2" + mime "^2.4.4" + qs "^6.9.1" + readable-stream "^3.4.0" + semver "^6.3.0" + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +through2@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" + integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== + dependencies: + readable-stream "2 || 3" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tough-cookie@^2.3.3: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +tslib@^1.9.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + 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.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +urijs@^1.18.12: + version "1.19.2" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.2.tgz#f9be09f00c4c5134b7cb3cf475c1dd394526265a" + integrity sha512-s/UIq9ap4JPZ7H1EB5ULo/aOUbWqfDi7FKzMC2Nz+0Si8GiT1rIEaprt8hy3Vy2Ex2aJPpOQv4P4DuOZ+K1c6w== + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + +uuid@^3.3.2, uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +vuvuzela@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vuvuzela/-/vuvuzela-1.0.3.tgz#3be145e58271c73ca55279dd851f12a682114b0b" + integrity sha1-O+FF5YJxxzylUnndhR8SpoIRSws= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-stream@~0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/write-stream/-/write-stream-0.4.3.tgz#83cc8c0347d0af6057a93862b4e3ae01de5c81c1" + integrity sha1-g8yMA0fQr2BXqThitOOuAd5cgcE= + dependencies: + readable-stream "~0.0.2" + +ws@^7.2.1: + version "7.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== + +xtend@^4.0.2, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yargs-parser@13.1.2, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.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 "^13.1.2"