Skip to content

Commit

Permalink
Merge pull request #117 from lifeomic/LO-5734
Browse files Browse the repository at this point in the history
LO-5734: Data Lake Query Commands
  • Loading branch information
mjtieman authored Oct 30, 2019
2 parents a8bdc85 + 7c6738a commit 3ae9a25
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 1 deletion.
10 changes: 10 additions & 0 deletions lib/cmds/data-lake.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

const options = require('../common-yargs');

exports.command = 'data-lake <command>';
exports.desc = 'Perform operations on the analytics data lake.';
exports.builder = yargs => {
return options(yargs.commandDir('data_lake_cmds'));
};
exports.handler = function (argv) {};
18 changes: 18 additions & 0 deletions lib/cmds/data_lake_cmds/get-query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

const { get } = require('../../api');
const print = require('../../print');

exports.command = 'get-query <queryId>';
exports.desc = 'Fetch a single query execution.';
exports.builder = yargs => {
yargs.positional('queryId', {
describe: 'Id of the query to fetch.',
type: 'string'
});
};

exports.handler = async argv => {
const response = await get(argv, `/v1/analytics/query/${argv.queryId}`);
print(response.data, argv);
};
37 changes: 37 additions & 0 deletions lib/cmds/data_lake_cmds/list-queries.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';

const { list } = require('../../api');
const print = require('../../print');
const querystring = require('querystring');

exports.command = 'list-queries <projectId>';
exports.desc = 'List the query executions in the project.';
exports.builder = yargs => {
yargs.positional('projectId', {
describe: 'The ID of the project to fetch queries of.',
type: 'string'
}).option('page-size', {
alias: 'n',
type: 'number',
default: 25,
describe: 'Maximum number of queries to return.'
}).option('next-page-token', {
alias: 't',
type: 'string',
describe: 'Token to retrieve the next page of results'
});
};

exports.handler = async argv => {
const query = {
datasetId: argv.projectId,
pageSize: argv.pageSize
};

if (argv.nextPageToken) {
query.nextPageToken = argv.nextPageToken;
}

const response = await list(argv, `/v1/analytics/query?${querystring.stringify(query)}`);
print(response.data, argv);
};
39 changes: 39 additions & 0 deletions lib/cmds/data_lake_cmds/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

const { post } = require('../../api');
const print = require('../../print');
const read = require('../../read');

exports.command = 'query <projectId>';
exports.desc = 'Submits a query to the Lifeomic data-lake API. A SQL query string can also be read from stdin.';
exports.builder = yargs => {
yargs.positional('projectId', {
describe: 'The ID of the project to search within.',
type: 'string'
}).option('query', {
alias: 'q',
type: 'string',
describe: 'The SQL query to run.'
}).option('output-file-name', {
alias: 'o',
type: 'string',
describe: 'Name of the results file.',
demandOption: true
});
};

exports.handler = async argv => {
const request = {
outputFileName: argv.outputFileName,
datasetId: argv.projectId
};

if (argv.query) {
request.query = argv.query;
} else {
request.query = await read(argv);
}

const response = await post(argv, '/v1/analytics/query', request);
print(response.data, argv);
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lifeomic/cli",
"version": "9.6.0",
"version": "9.7.0",
"description": "CLI for interacting with the LifeOmic PHC API.",
"main": "lo.js",
"author": "LifeOmic <[email protected]>",
Expand Down Expand Up @@ -43,6 +43,7 @@
"stream-chain": "^2.1.0",
"stream-csv-as-json": "^1.0.1",
"stream-json": "^1.2.1",
"uuid": "^3.3.2",
"yargs": "^12.0.5"
},
"devDependencies": {
Expand Down
133 changes: 133 additions & 0 deletions test/unit/commands/data-lake-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
'use strict';

const uuid = require('uuid');
const yargs = require('yargs');
const sinon = require('sinon');
const test = require('ava');
const proxyquire = require('proxyquire');

const getStub = sinon.stub();
const postStub = sinon.stub();
const listStub = sinon.stub();
const printSpy = sinon.spy();
const readStub = sinon.stub();
let callback;

const queryCmd = proxyquire('../../../lib/cmds/data_lake_cmds/query', {
'../../api': {
post: postStub
},
'../../print': (data, opts) => {
printSpy(data, opts);
callback();
},
'../../read': async () => readStub()
});

const listCmd = proxyquire('../../../lib/cmds/data_lake_cmds/list-queries', {
'../../api': {
list: listStub
},
'../../print': (data, opts) => {
printSpy(data, opts);
callback();
}
});

const getCmd = proxyquire('../../../lib/cmds/data_lake_cmds/get-query', {
'../../api': {
get: getStub
},
'../../print': (data, opts) => {
printSpy(data, opts);
callback();
}
});

test.afterEach.always(t => {
getStub.resetHistory();
postStub.resetHistory();
listStub.resetHistory();
printSpy.resetHistory();
readStub.resetHistory();
callback = null;
});

test.serial.cb('The "data-lake-query" command should accept a query as an optional argument', t => {
const query = "SELECT sample_id, gene, impact, amino_acid_change, histology FROM variant WHERE tumor_site='breast'";
const datasetId = uuid();
const outputFileName = 'data-lake-test';

postStub.onFirstCall().returns({});

callback = () => {
t.is(postStub.callCount, 1);
t.is(postStub.getCall(0).args[1], '/v1/analytics/query');
t.deepEqual(postStub.getCall(0).args[2], {
query: query,
datasetId: datasetId,
outputFileName: outputFileName
});
t.is(printSpy.callCount, 1);
t.end();
};

yargs.command(queryCmd).parse(`query ${datasetId} -q "${query}" -o ${outputFileName}`);
});

test.serial.cb('The "data-lake-query" command should accept a query from stdin', t => {
const query = "SELECT sample_id, gene, impact, amino_acid_change, histology FROM variant WHERE tumor_site='breast'";
const datasetId = uuid();
const outputFileName = 'data-lake-test';

postStub.onFirstCall().returns({});
readStub.onFirstCall().returns(query);

callback = () => {
t.is(postStub.callCount, 1);
t.is(postStub.getCall(0).args[1], '/v1/analytics/query');
t.deepEqual(postStub.getCall(0).args[2], {
query: query,
datasetId: datasetId,
outputFileName: outputFileName
});
t.is(printSpy.callCount, 1);
t.end();
};

yargs.command(queryCmd).parse(`query ${datasetId} -o ${outputFileName}`);
});

test.serial.cb('The "data-lake-list-queries" should accept page-size and next-page-token', t => {
const datasetId = uuid();
const pageSize = 30;
const nextPageToken = uuid();

listStub.onFirstCall().returns({});
const expectedPath = `/v1/analytics/query?datasetId=${datasetId}&pageSize=${pageSize}&nextPageToken=${nextPageToken}`;

callback = () => {
t.is(listStub.callCount, 1);
t.is(listStub.getCall(0).args[1], expectedPath);
t.is(printSpy.callCount, 1);
t.end();
};

yargs.command(listCmd).parse(`list-queries ${datasetId} -n ${pageSize} -t ${nextPageToken}`);
});

test.serial.cb('The "data-lake-get-query" should add query-id to path', t => {
const queryId = uuid();

getStub.onFirstCall().returns({});
const expectedPath = `/v1/analytics/query/${queryId}`;

callback = () => {
t.is(getStub.callCount, 1);
t.is(getStub.getCall(0).args[1], expectedPath);
t.is(printSpy.callCount, 1);
t.end();
};

yargs.command(getCmd).parse(`get-query ${queryId}`);
});

0 comments on commit 3ae9a25

Please sign in to comment.