diff --git a/app/components/Settings/Preview/TableTree.react.js b/app/components/Settings/Preview/TableTree.react.js
index b267ab05d..2d268fa5b 100644
--- a/app/components/Settings/Preview/TableTree.react.js
+++ b/app/components/Settings/Preview/TableTree.react.js
@@ -4,6 +4,7 @@ import TreeView from 'react-treeview';
import {isEmpty, has} from 'ramda';
import {DIALECTS} from '../../../constants/constants';
+import {getPathNames} from '../../../utils/utils';
const BASENAME_RE = /[^\\/]+$/;
@@ -26,6 +27,17 @@ class TableTree extends Component {
})
}
+ getLabel(connectionObject) {
+ switch (connectionObject.dialect) {
+ case DIALECTS.SQLITE:
+ return BASENAME_RE.exec(connectionObject.storage)[0] || connectionObject.storage;
+ case DIALECTS.DATA_WORLD:
+ return getPathNames(connectionObject.url)[2];
+ default:
+ return connectionObject.database;
+ }
+ }
+
storeSchemaTree() {
const {schemaRequest, getSqlSchema, updatePreview} = this.props;
@@ -84,9 +96,7 @@ class TableTree extends Component {
return (
{'Updating'}
);
}
- const label = (this.props.connectionObject.dialect === DIALECTS.SQLITE) ?
- BASENAME_RE.exec(this.props.connectionObject.storage)[0] || this.props.connectionObject.storage :
- this.props.connectionObject.database;
+ const label = this.getLabel(this.props.connectionObject);
const labelNode = {label};
return (
diff --git a/app/components/Settings/Settings.react.js b/app/components/Settings/Settings.react.js
index 2b15aff0f..e22abdfbb 100644
--- a/app/components/Settings/Settings.react.js
+++ b/app/components/Settings/Settings.react.js
@@ -199,7 +199,8 @@ class Settings extends Component {
DIALECTS.APACHE_SPARK,
DIALECTS.IBM_DB2,
DIALECTS.MYSQL, DIALECTS.MARIADB, DIALECTS.POSTGRES,
- DIALECTS.REDSHIFT, DIALECTS.MSSQL, DIALECTS.SQLITE
+ DIALECTS.REDSHIFT, DIALECTS.MSSQL, DIALECTS.SQLITE,
+ DIALECTS.DATA_WORLD
])) {
if (connectRequest.status === 200 && !tablesRequest.status) {
this.setState({editMode: false});
diff --git a/app/components/Settings/Tabs/Tab.react.js b/app/components/Settings/Tabs/Tab.react.js
index 70d3b6b94..21a65d5ff 100644
--- a/app/components/Settings/Tabs/Tab.react.js
+++ b/app/components/Settings/Tabs/Tab.react.js
@@ -2,6 +2,7 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {LOGOS, DIALECTS} from '../../../constants/constants';
+import {getPathNames} from '../../../utils/utils';
export default class ConnectionTab extends Component {
constructor(props) {
@@ -35,6 +36,13 @@ export default class ConnectionTab extends Component {
label = `Elasticsearch (${connectionObject.host})`;
} else if (connectionObject.dialect === DIALECTS.SQLITE) {
label = connectionObject.storage;
+ } else if (connectionObject.dialect === DIALECTS.DATA_WORLD) {
+ const pathNames = getPathNames(connectionObject.url);
+ if (pathNames.length >= 3) {
+ label = `data.world (${pathNames[1]}/${pathNames[2]})`;
+ } else {
+ label = 'data.world (/)';
+ }
} else {
label = `${connectionObject.database} (${connectionObject.username}@${connectionObject.host})`;
}
diff --git a/app/constants/constants.js b/app/constants/constants.js
index df4ac8079..e233a4e88 100644
--- a/app/constants/constants.js
+++ b/app/constants/constants.js
@@ -14,7 +14,8 @@ export const DIALECTS = {
IBM_DB2: 'ibm db2',
APACHE_SPARK: 'apache spark',
APACHE_IMPALA: 'apache impala',
- APACHE_DRILL: 'apache drill'
+ APACHE_DRILL: 'apache drill',
+ DATA_WORLD: 'data.world'
};
export const SQL_DIALECTS_USING_EDITOR = [
@@ -26,7 +27,8 @@ export const SQL_DIALECTS_USING_EDITOR = [
'sqlite',
'ibm db2',
'apache spark',
- 'apache impala'
+ 'apache impala',
+ 'data.world'
];
const commonSqlOptions = [
@@ -178,7 +180,21 @@ export const CONNECTION_CONFIG = {
'value': 'secretAccessKey',
'type': 'password'
}
- ] // TODO - password options for apache drill
+ ], // TODO - password options for apache drill
+ [DIALECTS.DATA_WORLD]: [
+ {
+ 'label': 'Dataset/Project URL',
+ 'value': 'url',
+ 'type': 'text',
+ 'description': 'The URL of the dataset or project on data.world'
+ },
+ {
+ 'label': 'Read/Write API Token',
+ 'value': 'token',
+ 'type': 'password',
+ 'description': 'Your data.world read/write token. It can be obtained from https://data.world/settings/advanced'
+ }
+ ]
};
@@ -194,7 +210,8 @@ export const LOGOS = {
[DIALECTS.MSSQL]: 'images/mssql-logo.png',
[DIALECTS.SQLITE]: 'images/sqlite-logo.png',
[DIALECTS.S3]: 'images/s3-logo.png',
- [DIALECTS.APACHE_DRILL]: 'images/apache_drill-logo.png'
+ [DIALECTS.APACHE_DRILL]: 'images/apache_drill-logo.png',
+ [DIALECTS.DATA_WORLD]: 'images/dataworld-logo.png'
};
export function PREVIEW_QUERY (dialect, table, database = '') {
@@ -207,6 +224,7 @@ export function PREVIEW_QUERY (dialect, table, database = '') {
case DIALECTS.SQLITE:
case DIALECTS.MARIADB:
case DIALECTS.POSTGRES:
+ case DIALECTS.DATA_WORLD:
case DIALECTS.REDSHIFT:
return `SELECT * FROM ${table} LIMIT 1000`;
case DIALECTS.MSSQL:
@@ -378,5 +396,8 @@ export const SAMPLE_DBS = {
sqlite: {
dialect: 'sqlite',
storage: `${__dirname}/plotly_datasets.db`
+ },
+ [DIALECTS.DATA_WORLD]: {
+ url: 'https://data.world/rflprr/reported-lyme-disease-cases-by-state'
}
};
diff --git a/app/images/dataworld-logo.png b/app/images/dataworld-logo.png
new file mode 100644
index 000000000..3ca880384
Binary files /dev/null and b/app/images/dataworld-logo.png differ
diff --git a/app/utils/utils.js b/app/utils/utils.js
index e3d4f6488..5b030271e 100644
--- a/app/utils/utils.js
+++ b/app/utils/utils.js
@@ -49,3 +49,11 @@ export function homeUrl() {
'/external-data-connector' :
'';
}
+
+export function getPathNames(url) {
+ const parser = document.createElement('a');
+ parser.href = url;
+ const pathNames = parser.pathname.split('/');
+
+ return pathNames;
+}
diff --git a/backend/persistent/datastores/Datastores.js b/backend/persistent/datastores/Datastores.js
index 075660593..17be6cad8 100644
--- a/backend/persistent/datastores/Datastores.js
+++ b/backend/persistent/datastores/Datastores.js
@@ -5,6 +5,7 @@ import * as ApacheDrill from './ApacheDrill';
import * as IbmDb2 from './ibmdb2';
import * as ApacheLivy from './livy';
import * as ApacheImpala from './impala';
+import * as DataWorld from './dataworld';
import * as DatastoreMock from './datastoremock';
/*
@@ -45,6 +46,8 @@ function getDatastoreClient(connection) {
return ApacheImpala;
} else if (dialect === 'ibm db2') {
return IbmDb2;
+ } else if (dialect === 'data.world') {
+ return DataWorld;
}
return Sql;
}
diff --git a/backend/persistent/datastores/dataworld.js b/backend/persistent/datastores/dataworld.js
new file mode 100644
index 000000000..00e336c64
--- /dev/null
+++ b/backend/persistent/datastores/dataworld.js
@@ -0,0 +1,111 @@
+import fetch from 'node-fetch';
+import url from 'url';
+
+import Logger from '../../logger';
+
+function parseUrl(datasetUrl) {
+ const pathnameArray = url.parse(datasetUrl).pathname.split('/');
+ return {
+ owner: pathnameArray[1],
+ id: pathnameArray[2]
+ };
+}
+
+export function connect(connection) {
+ const { owner, id } = parseUrl(connection.url);
+ return fetch(`https://api.data.world/v0/datasets/${owner}/${id}/`, {
+ method: 'GET',
+ headers: {
+ 'Authorization': `Bearer ${connection.token}`,
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
+ 'User-Agent': `Falcon/Plotly - ${process.env.npm_package_version}`
+ }
+ })
+ .then(res => res.json())
+ .then(json => {
+ // json.code is defined only when there is an error
+ if (json.code) {
+ throw new Error(JSON.stringify(json));
+ }
+ })
+ .catch(err => {
+ Logger.log(err);
+ throw err;
+ });
+}
+
+export function tables(connection) {
+ return query('SELECT * FROM Tables', connection).then((res) => {
+ const allTables = res.rows.map((table) => {
+ return table[0].replace(/-/g, '_');
+ });
+
+ return allTables;
+ })
+ .catch(err => {
+ Logger.log(err);
+ throw err;
+ });
+}
+
+export function schemas(connection) {
+ return query('SELECT * FROM TableColumns', connection).then((res) => {
+ const rows = res.rows.map((table) => {
+ const tableName = table[0].replace(/-/g, '_');
+ const columnName = table[3];
+ // Extract the datatype from datatype url e.g. http://www.w3.org/2001/XMLSchema#integer
+ const columnDataType = /#(.*)/.exec(table[6])[1];
+
+ return [
+ tableName,
+ columnName,
+ columnDataType
+ ];
+ });
+
+ return ({
+ columnNames: [ 'tablename', 'column_name', 'data_type' ],
+ rows
+ });
+ })
+ .catch(err => {
+ Logger.log(err);
+ throw err;
+ });
+}
+
+export function query(queryString, connection) {
+ const { owner, id } = parseUrl(connection.url);
+ const params = `${encodeURIComponent('query')}=${encodeURIComponent(queryString)}`;
+
+ return fetch(`https://api.data.world/v0/sql/${owner}/${id}?includeTableSchema=true`, {
+ method: 'POST',
+ headers: {
+ 'Authorization': `Bearer ${connection.token}`,
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
+ 'User-Agent': `Falcon/Plotly - ${process.env.npm_package_version}`
+ },
+ body: params
+ })
+ .then(res => {
+ return res.json();
+ })
+ .then(json => {
+ const fields = json[0].fields;
+ const columnnames = fields.map((field) => {
+ return field.name;
+ });
+ const rows = json.slice(1).map((row) => {
+ return Object.values(row);
+ });
+
+ return ({
+ columnnames,
+ rows
+ });
+ })
+ .catch(err => {
+ Logger.log(err);
+ throw err;
+ });
+}
diff --git a/package.json b/package.json
index e6a502aea..3bffdbd17 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"test-unit-all-watch": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --bail --full-trace --timeout 90000 --compilers js:babel-register --recursive test/**/*.spec.js --watch",
"test-unit-watch": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --bail --full-trace --timeout 90000 --watch --compilers js:babel-register ",
"test-unit-certificates": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --full-trace --timeout 90000 --compilers js:babel-register test/backend/certificates.spec.js",
+ "test-unit-dataworld": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --full-trace --timeout 90000 --compilers js:babel-register test/backend/datastores.dataworld.spec.js",
"test-unit-ibmdb": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --full-trace --timeout 90000 --compilers js:babel-register test/backend/datastores.ibmdb.spec.js",
"test-unit-impala": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --full-trace --timeout 90000 --compilers js:babel-register test/backend/datastores.impala.spec.js",
"test-unit-livy": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 electron-mocha --full-trace --timeout 90000 --compilers js:babel-register test/backend/datastores.livy.spec.js",
@@ -168,6 +169,7 @@
"json-loader": "^0.5.4",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
+ "nock": "^9.1.5",
"node-fetch": "^1.7.2",
"node-impala": "^2.0.4",
"plotly.js": "^1.31.2",
diff --git a/test/backend/datastores.dataworld.spec.js b/test/backend/datastores.dataworld.spec.js
new file mode 100644
index 000000000..b21932be0
--- /dev/null
+++ b/test/backend/datastores.dataworld.spec.js
@@ -0,0 +1,109 @@
+import nock from 'nock';
+import {assert} from 'chai';
+
+import {
+ dataWorldConnection as connection,
+ dataWorldTablesResponse,
+ dataWorldQueryResponse,
+ dataWorldColumnsResponse
+} from './utils.js';
+import {connect, tables, query, schemas} from '../../backend/persistent/datastores/dataworld';
+
+// Mock dataset GET request
+nock('https://api.data.world/v0/datasets/falcon/test-dataset', {
+ reqheaders: {
+ 'Authorization': 'Bearer token',
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
+ }
+})
+.get('/')
+.reply(200, {
+ owner: 'falcon',
+ id: 'test-dataset',
+ title: 'test-dataset',
+ tags: [],
+ visibility: 'PUBLIC',
+ files: []
+});
+
+// Mock table query POST request
+nock('https://api.data.world', {
+ reqheaders: {
+ 'Authorization': 'Bearer token',
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
+ }
+})
+.post('/v0/sql/falcon/test-dataset', 'query=SELECT%20*%20FROM%20Tables')
+.query({
+ includeTableSchema: 'true'
+})
+.reply(200, dataWorldTablesResponse);
+
+// Mock query POST request
+nock('https://api.data.world', {
+ reqheaders: {
+ 'Authorization': 'Bearer token',
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
+ }
+})
+.post('/v0/sql/falcon/test-dataset', 'query=SELECT%20*%20FROM%20sampletable%20LIMIT%205')
+.query({
+ includeTableSchema: 'true'
+})
+.reply(200, dataWorldQueryResponse);
+
+// Mock table columns query POST request
+nock('https://api.data.world', {
+ reqheaders: {
+ 'Authorization': 'Bearer token',
+ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
+ }
+})
+.post('/v0/sql/falcon/test-dataset', 'query=SELECT%20*%20FROM%20TableColumns')
+.query({
+ includeTableSchema: 'true'
+})
+.reply(200, dataWorldColumnsResponse);
+
+describe('Data World:', function () {
+
+ it('connect succeeds', function() {
+ return connect(connection);
+ });
+
+ it('tables returns list of tables', function() {
+ return tables(connection).then(result => {
+ assert.deepEqual(result, ['sampletable']);
+ });
+ });
+
+ it('query returns rows and column names', function() {
+ return query('SELECT * FROM sampletable LIMIT 5', connection).then(result => {
+ assert.deepEqual(
+ result.columnnames,
+ [ 'stringcolumn', 'datecolumn', 'decimalcolumn' ]
+ );
+ assert.deepEqual(result.rows, [
+ [ 'First column', '2017-05-24', 1 ],
+ [ 'Second column', '2017-05-25', 2 ],
+ [ 'Third column', '2017-05-26', 3 ],
+ [ 'Fourth column', '2017-05-27', 4 ],
+ [ 'Fifth column', '2017-05-28', 5 ]
+ ]);
+ });
+ });
+
+ it('schemas returns table schemas', function() {
+ return schemas(connection).then(result => {
+ assert.deepEqual(
+ result.columnNames,
+ [ 'tablename', 'column_name', 'data_type' ]
+ );
+ assert.deepEqual(result.rows, [
+ [ 'sampletable', 'stringcolumn', 'string' ],
+ [ 'sampletable', 'datecolumn', 'date' ],
+ [ 'sampletable', 'decimalcolumn', 'decimal' ]
+ ]);
+ });
+ });
+});
diff --git a/test/backend/utils.js b/test/backend/utils.js
index 7c63d8552..6fb07ed17 100644
--- a/test/backend/utils.js
+++ b/test/backend/utils.js
@@ -296,6 +296,10 @@ export const apacheImpalaConnection = {
port: 21000,
database: 'plotly'
};
+export const dataWorldConnection = {
+ url: 'https://data.world/falcon/test-dataset',
+ token: 'token'
+};
// TODO - Add sqlite here
// TODO - Add postgis in here
@@ -602,3 +606,188 @@ export const apacheDrillStorage = [
}
}
];
+
+export const dataWorldTablesResponse = [
+ {
+ 'fields': [
+ {
+ 'name': 'tableId',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'tableName',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'tableTitle',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'tableDescription',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'owner',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'dataset',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ }
+ ]
+ },
+ {
+ 'tableId': 'sampletable',
+ 'tableName': 'sampletable',
+ 'tableTitle': 'sampletable',
+ 'tableDescription': null,
+ 'owner': 'falcon',
+ 'dataset': 'sample-dataset'
+ }
+];
+
+export const dataWorldQueryResponse = [
+ {
+ 'fields': [
+ {
+ 'name': 'stringcolumn',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'datecolumn',
+ 'type': 'date',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#date'
+ },
+ {
+ 'name': 'decimalcolumn',
+ 'type': 'decimal',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#decimal'
+ }
+ ]
+ },
+ {
+ 'stringcolumn': 'First column',
+ 'datecolumn': '2017-05-24',
+ 'decimalcolumn': 1
+ },
+ {
+ 'stringcolumn': 'Second column',
+ 'datecolumn': '2017-05-25',
+ 'decimalcolumn': 2
+ },
+ {
+ 'stringcolumn': 'Third column',
+ 'datecolumn': '2017-05-26',
+ 'decimalcolumn': 3
+ },
+ {
+ 'stringcolumn': 'Fourth column',
+ 'datecolumn': '2017-05-27',
+ 'decimalcolumn': 4
+ },
+ {
+ 'stringcolumn': 'Fifth column',
+ 'datecolumn': '2017-05-28',
+ 'decimalcolumn': 5
+ }
+];
+
+export const dataWorldColumnsResponse = [
+ {
+ 'fields': [
+ {
+ 'name': 'tableId',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'tableName',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'columnIndex',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'columnName',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'columnTitle',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'columnDescription',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'columnDatatype',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'columnNullable',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'owner',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ },
+ {
+ 'name': 'dataset',
+ 'type': 'string',
+ 'rdfType': 'http://www.w3.org/2001/XMLSchema#string'
+ }
+ ]
+ },
+ {
+ 'tableId': 'sampletable',
+ 'tableName': 'sampletable',
+ 'columnIndex': 1,
+ 'columnName': 'stringcolumn',
+ 'columnTitle': 'stringcolumn',
+ 'columnDescription': null,
+ 'columnDatatype': 'http://www.w3.org/2001/XMLSchema#string',
+ 'columnNullable': false,
+ 'owner': 'falcon',
+ 'dataset': 'test-dataset'
+ },
+ {
+ 'tableId': 'sampletable',
+ 'tableName': 'sampletable',
+ 'columnIndex': 2,
+ 'columnName': 'datecolumn',
+ 'columnTitle': 'datecolumn',
+ 'columnDescription': null,
+ 'columnDatatype': 'http://www.w3.org/2001/XMLSchema#date',
+ 'columnNullable': false,
+ 'owner': 'falcon',
+ 'dataset': 'test-dataset'
+ },
+ {
+ 'tableId': 'sampletable',
+ 'tableName': 'sampletable',
+ 'columnIndex': 3,
+ 'columnName': 'decimalcolumn',
+ 'columnTitle': 'decimalcolumn',
+ 'columnDescription': null,
+ 'columnDatatype': 'http://www.w3.org/2001/XMLSchema#decimal',
+ 'columnNullable': false,
+ 'owner': 'falcon',
+ 'dataset': 'test-dataset'
+ }
+];
diff --git a/yarn.lock b/yarn.lock
index a17679578..1d4cbc115 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1659,7 +1659,7 @@ chai-spies@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/chai-spies/-/chai-spies-0.7.1.tgz#343d99f51244212e8b17e64b93996ff7b2c2a9b1"
-chai@^3.5.0:
+"chai@>=1.9.2 <4.0.0", chai@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247"
dependencies:
@@ -5008,7 +5008,7 @@ json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
-json-stringify-safe@~5.0.1:
+json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -5338,7 +5338,7 @@ lodash@4.12.0:
version "4.12.0"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.12.0.tgz#2bd6dc46a040f59e686c972ed21d93dc59053258"
-lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1:
+lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.6.1, lodash@~4.17.2:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -5830,6 +5830,20 @@ nextafter@^1.0.0:
dependencies:
double-bits "^1.1.0"
+nock@^9.1.5:
+ version "9.1.5"
+ resolved "https://registry.yarnpkg.com/nock/-/nock-9.1.5.tgz#9e4878e0e1c050bdd93ae1e326e89461ea15618b"
+ dependencies:
+ chai ">=1.9.2 <4.0.0"
+ debug "^2.2.0"
+ deep-equal "^1.0.0"
+ json-stringify-safe "^5.0.1"
+ lodash "~4.17.2"
+ mkdirp "^0.5.0"
+ propagate "0.4.0"
+ qs "^6.5.1"
+ semver "^5.3.0"
+
node-abi@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.1.2.tgz#4da6caceb6685fcd31e7dd1994ef6bb7d0a9c0b2"
@@ -6814,6 +6828,10 @@ prop-types@^15.0.0, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4,
loose-envify "^1.3.1"
object-assign "^4.1.1"
+propagate@0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/propagate/-/propagate-0.4.0.tgz#f3fcca0a6fe06736a7ba572966069617c130b481"
+
protocol-buffers-schema@^2.0.2:
version "2.2.0"
resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-2.2.0.tgz#d29c6cd73fb655978fb6989691180db844119f61"
@@ -6874,7 +6892,7 @@ q@~1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
-qs@^6.2.1, qs@~6.5.1:
+qs@^6.2.1, qs@^6.5.1, qs@~6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"