From 37329673c4fa41053ec0a0038f13711c1e35b5b2 Mon Sep 17 00:00:00 2001 From: Connor Luebbehusen Date: Tue, 3 Jan 2023 13:15:30 -0500 Subject: [PATCH 1/4] PHC-3861 - add get and list commands --- lib/cmds/genomics_cmds/ingestions.js | 10 ++++ lib/cmds/genomics_cmds/ingestions_cmds/get.js | 21 ++++++++ .../genomics_cmds/ingestions_cmds/list.js | 54 +++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 lib/cmds/genomics_cmds/ingestions.js create mode 100644 lib/cmds/genomics_cmds/ingestions_cmds/get.js create mode 100644 lib/cmds/genomics_cmds/ingestions_cmds/list.js diff --git a/lib/cmds/genomics_cmds/ingestions.js b/lib/cmds/genomics_cmds/ingestions.js new file mode 100644 index 0000000..d2f2a82 --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions.js @@ -0,0 +1,10 @@ +'use strict'; + +const options = require('../../common-yargs'); + +exports.command = 'ingestions '; +exports.desc = 'Perform operations on genomic ingestions.'; +exports.builder = yargs => { + return options(yargs.commandDir('ingestions_cmds')); +}; +exports.handler = function (argv) {}; diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/get.js b/lib/cmds/genomics_cmds/ingestions_cmds/get.js new file mode 100644 index 0000000..18d9cc8 --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions_cmds/get.js @@ -0,0 +1,21 @@ +'use strict'; + +const { get } = require('../../../api'); +const print = require('../../../print'); + +exports.command = 'get '; +exports.desc = 'Fetch ingestion details by and '; +exports.builder = yargs => { + yargs.positional('projectId', { + describe: 'The project ID.', + type: 'string' + }).positional('ingestionId', { + describe: 'The ingestion ID.', + type: 'string' + }); +}; + +exports.handler = async argv => { + const response = await get(argv, `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions/${argv.ingestionId}`); + print(response.data, argv); +}; diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/list.js b/lib/cmds/genomics_cmds/ingestions_cmds/list.js new file mode 100644 index 0000000..70ce599 --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions_cmds/list.js @@ -0,0 +1,54 @@ +'use strict'; + +const querystring = require('querystring'); +const { get, list } = require('../../../api'); +const print = require('../../../print'); +const formatPage = require('../../../formatPage'); + +exports.command = 'list '; +exports.desc = 'List ingestions by '; +exports.builder = yargs => { + yargs.positional('projectId', { + describe: 'The project ID.', + type: 'string' + }).option('page-size', { + describe: 'The page size.', + type: 'number', + alias: 'n', + default: 25 + }).option('next-page-token', { + describe: 'The next page token.', + alias: 't', + type: 'string' + }).option('limit', { + describe: 'The maximum number of items to return.', + alias: 'l', + type: 'number' + }).option('name', { + describe: 'Filter ingestions where the ingestion name contains this', + type: 'string' + }).option('failed', { + describe: 'Filter ingestions that have failed', + type: 'boolean' + }).option('step', { + describe: 'Filter ingestions that are on this step', + type: 'string', + choices: ['AwaitingFiles', 'Submitted', 'Transformed', 'Normalized', 'TestCreated', 'TestNotCreated'] + }); +}; + +exports.handler = async argv => { + const opts = { + projectId: argv.projectId, + pageSize: argv.limit ? 1000 : argv.pageSize, + nextPageToken: argv.nextPageToken, + name: argv.name, + failed: argv.failed, + steps: argv.step + }; + + const path = `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions?${querystring.stringify(opts)}`; + + const response = await (argv.limit ? list(argv, path) : get(argv, path)); + print(formatPage(response.data), argv); +}; From 23ce17e543b173e84a3493a6c8cba09223464a52 Mon Sep 17 00:00:00 2001 From: Connor Luebbehusen Date: Tue, 3 Jan 2023 15:26:19 -0500 Subject: [PATCH 2/4] PHC-3861 - add commands to create ingestions --- .../ingestions_cmds/create-caris-bam.js | 36 +++++++++++++++ .../ingestions_cmds/create-caris.js | 36 +++++++++++++++ .../ingestions_cmds/create-foundation-bam.js | 36 +++++++++++++++ .../ingestions_cmds/create-foundation.js | 44 +++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 lib/cmds/genomics_cmds/ingestions_cmds/create-caris-bam.js create mode 100644 lib/cmds/genomics_cmds/ingestions_cmds/create-caris.js create mode 100644 lib/cmds/genomics_cmds/ingestions_cmds/create-foundation-bam.js create mode 100644 lib/cmds/genomics_cmds/ingestions_cmds/create-foundation.js diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/create-caris-bam.js b/lib/cmds/genomics_cmds/ingestions_cmds/create-caris-bam.js new file mode 100644 index 0000000..441fbcb --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions_cmds/create-caris-bam.js @@ -0,0 +1,36 @@ +'use strict'; + +const { post } = require('../../../api'); +const print = require('../../../print'); + +exports.command = 'create-caris-bam '; +exports.desc = 'Create Caris BAM ingestion for in '; +exports.builder = yargs => { + yargs.positional('projectId', { + describe: 'The project ID.', + type: 'string' + }).positional('bamFileId', { + describe: 'The ID of the BAM file.', + type: 'string' + }).option('succeededEmail', { + describe: 'An email address to notify if the ingestion succeeds', + type: 'string' + }).option('failedEmail', { + describe: 'An email address to notify if the ingestion fails', + type: 'string' + }); +}; + +exports.handler = async argv => { + const response = await post(argv, `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions`, { + ingestionType: 'CarisBam', + inputFiles: { + bam: argv.bamFileId + }, + notificationConfig: { + succeededEmail: argv.succeededEmail, + failedEmail: argv.failedEmail + } + }); + print(response.data, argv); +}; diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/create-caris.js b/lib/cmds/genomics_cmds/ingestions_cmds/create-caris.js new file mode 100644 index 0000000..fe29f96 --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions_cmds/create-caris.js @@ -0,0 +1,36 @@ +'use strict'; + +const { post } = require('../../../api'); +const print = require('../../../print'); + +exports.command = 'create-caris '; +exports.desc = 'Create Caris ingestion for in '; +exports.builder = yargs => { + yargs.positional('projectId', { + describe: 'The project ID.', + type: 'string' + }).positional('tarFileId', { + describe: 'The ID of the TAR file.', + type: 'string' + }).option('succeededEmail', { + describe: 'An email address to notify if the ingestion succeeds', + type: 'string' + }).option('failedEmail', { + describe: 'An email address to notify if the ingestion fails', + type: 'string' + }); +}; + +exports.handler = async argv => { + const response = await post(argv, `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions`, { + ingestionType: 'Caris', + inputFiles: { + tar: argv.tarFileId + }, + notificationConfig: { + succeededEmail: argv.succeededEmail, + failedEmail: argv.failedEmail + } + }); + print(response.data, argv); +}; diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/create-foundation-bam.js b/lib/cmds/genomics_cmds/ingestions_cmds/create-foundation-bam.js new file mode 100644 index 0000000..fe9b00e --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions_cmds/create-foundation-bam.js @@ -0,0 +1,36 @@ +'use strict'; + +const { post } = require('../../../api'); +const print = require('../../../print'); + +exports.command = 'create-foundation-bam '; +exports.desc = 'Create Foundation BAM ingestion for in '; +exports.builder = yargs => { + yargs.positional('projectId', { + describe: 'The project ID.', + type: 'string' + }).positional('bamFileId', { + describe: 'The ID of the BAM file.', + type: 'string' + }).option('succeededEmail', { + describe: 'An email address to notify if the ingestion succeeds', + type: 'string' + }).option('failedEmail', { + describe: 'An email address to notify if the ingestion fails', + type: 'string' + }); +}; + +exports.handler = async argv => { + const response = await post(argv, `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions`, { + ingestionType: 'FoundationBam', + inputFiles: { + bam: argv.bamFileId + }, + notificationConfig: { + succeededEmail: argv.succeededEmail, + failedEmail: argv.failedEmail + } + }); + print(response.data, argv); +}; diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/create-foundation.js b/lib/cmds/genomics_cmds/ingestions_cmds/create-foundation.js new file mode 100644 index 0000000..44ee194 --- /dev/null +++ b/lib/cmds/genomics_cmds/ingestions_cmds/create-foundation.js @@ -0,0 +1,44 @@ +'use strict'; + +const { post } = require('../../../api'); +const print = require('../../../print'); + +exports.command = 'create-foundation [vcfFileId]'; +exports.desc = 'Create Foundation ingestion for , , and optionally [vcfFileId] in '; +exports.builder = yargs => { + yargs.positional('projectId', { + describe: 'The project ID.', + type: 'string' + }).positional('xmlFileId', { + describe: 'The ID of the XML file.', + type: 'string' + }).positional('reportFileId', { + describe: 'The ID of the report file.', + type: 'string' + }).positional('vcfFileId', { + describe: 'The ID of the VCF file.', + type: 'string' + }).option('succeededEmail', { + describe: 'An email address to notify if the ingestion succeeds', + type: 'string' + }).option('failedEmail', { + describe: 'An email address to notify if the ingestion fails', + type: 'string' + }); +}; + +exports.handler = async argv => { + const response = await post(argv, `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions`, { + ingestionType: 'Foundation', + inputFiles: { + xml: argv.xmlFileId, + vcf: argv.vcfFileId || null, + report: argv.reportFileId + }, + notificationConfig: { + succeededEmail: argv.succeededEmail, + failedEmail: argv.failedEmail + } + }); + print(response.data, argv); +}; From 0c3cf86181b1fbb8ae2347ae0f60bd5786d00de5 Mon Sep 17 00:00:00 2001 From: Connor Luebbehusen Date: Wed, 4 Jan 2023 09:57:19 -0500 Subject: [PATCH 3/4] feat: PHC-3861 - add tests for ingestion commands --- .../genomics_cmds/ingestions_cmds/list.js | 12 +- .../unit/commands/genomics-ingestions.test.js | 162 ++++++++++++++++++ 2 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 test/unit/commands/genomics-ingestions.test.js diff --git a/lib/cmds/genomics_cmds/ingestions_cmds/list.js b/lib/cmds/genomics_cmds/ingestions_cmds/list.js index 70ce599..c4e68e0 100644 --- a/lib/cmds/genomics_cmds/ingestions_cmds/list.js +++ b/lib/cmds/genomics_cmds/ingestions_cmds/list.js @@ -39,14 +39,14 @@ exports.builder = yargs => { exports.handler = async argv => { const opts = { - projectId: argv.projectId, - pageSize: argv.limit ? 1000 : argv.pageSize, - nextPageToken: argv.nextPageToken, - name: argv.name, - failed: argv.failed, - steps: argv.step + pageSize: argv.limit ? 1000 : argv.pageSize }; + if (argv.nextPageToken) opts.nextPageToken = argv.nextPageToken; + if (argv.name) opts.name = argv.name; + if (argv.failed) opts.failed = argv.failed; + if (argv.step) opts.steps = argv.step; + const path = `/v1/genomic-ingestion/projects/${argv.projectId}/ingestions?${querystring.stringify(opts)}`; const response = await (argv.limit ? list(argv, path) : get(argv, path)); diff --git a/test/unit/commands/genomics-ingestions.test.js b/test/unit/commands/genomics-ingestions.test.js new file mode 100644 index 0000000..2ab238d --- /dev/null +++ b/test/unit/commands/genomics-ingestions.test.js @@ -0,0 +1,162 @@ +'use strict'; + +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 printSpy = sinon.stub(); +let callback; + +const mocks = { + '../../../api': { + get: getStub, + post: postStub + }, + '../../../print': (data, opts) => { + printSpy(data, opts); + callback(); + } +}; + +const get = proxyquire('../../../lib/cmds/genomics_cmds/ingestions_cmds/get', mocks); +const list = proxyquire('../../../lib/cmds/genomics_cmds/ingestions_cmds/list', mocks); +const createFoundation = proxyquire('../../../lib/cmds/genomics_cmds/ingestions_cmds/create-foundation', mocks); +const createCaris = proxyquire('../../../lib/cmds/genomics_cmds/ingestions_cmds/create-caris', mocks); +const createFoundationBam = proxyquire('../../../lib/cmds/genomics_cmds/ingestions_cmds/create-foundation-bam', mocks); +const createCarisBam = proxyquire('../../../lib/cmds/genomics_cmds/ingestions_cmds/create-caris-bam', mocks); + +test.always.afterEach(t => { + getStub.resetHistory(); + postStub.resetHistory(); + printSpy.resetHistory(); + callback = null; +}); + +test.serial.cb('The "get" command should get an ingestion based on project id and ingestion id', t => { + const res = { data: { id: 'ingestionId' } }; + getStub.onFirstCall().returns(res); + callback = () => { + t.is(getStub.callCount, 1); + t.is(getStub.getCall(0).args[1], '/v1/genomic-ingestion/projects/projectId/ingestions/ingestionId'); + t.is(printSpy.callCount, 1); + t.true(printSpy.calledWith({ id: 'ingestionId' })); + t.end(); + }; + + yargs.command(get).parse('get projectId ingestionId'); +}); + +test.serial.cb('The "list" command should return ingestions based on project id', t => { + const res = { data: { items: [] } }; + getStub.onFirstCall().returns(res); + callback = () => { + t.is(getStub.callCount, 1); + t.is(getStub.getCall(0).args[1], '/v1/genomic-ingestion/projects/projectId/ingestions?pageSize=1&nextPageToken=token&name=foo&steps=Submitted'); + t.is(printSpy.callCount, 1); + t.true(printSpy.calledWith({ items: [] })); + t.end(); + }; + + yargs.command(list).parse('list projectId -n 1 -t token --name foo --failed false --step Submitted'); +}); + +test.serial.cb('The "create-foundation" command should create a Foundation ingestion', t => { + const res = { data: { id: 'ingestionId' } }; + postStub.onFirstCall().returns(res); + callback = () => { + t.is(postStub.callCount, 1); + t.is(postStub.getCall(0).args[1], '/v1/genomic-ingestion/projects/projectId/ingestions'); + t.deepEqual(postStub.getCall(0).args[2], { + ingestionType: 'Foundation', + inputFiles: { + xml: 'xmlFileId', + vcf: 'vcfFileId', + report: 'reportFileId' + }, + notificationConfig: { + succeededEmail: 'succeeded@test.com', + failedEmail: 'failed@test.com' + } + }); + t.is(printSpy.callCount, 1); + t.true(printSpy.calledWith({ id: 'ingestionId' })); + t.end(); + }; + + yargs.command(createFoundation).parse('create-foundation projectId xmlFileId reportFileId vcfFileId --succeededEmail succeeded@test.com --failedEmail failed@test.com'); +}); + +test.serial.cb('The "create-caris" command should create a Caris ingestion', t => { + const res = { data: { id: 'ingestionId' } }; + postStub.onFirstCall().returns(res); + callback = () => { + t.is(postStub.callCount, 1); + t.is(postStub.getCall(0).args[1], '/v1/genomic-ingestion/projects/projectId/ingestions'); + t.deepEqual(postStub.getCall(0).args[2], { + ingestionType: 'Caris', + inputFiles: { + tar: 'tarFileId' + }, + notificationConfig: { + succeededEmail: undefined, + failedEmail: undefined + } + }); + t.is(printSpy.callCount, 1); + t.true(printSpy.calledWith({ id: 'ingestionId' })); + t.end(); + }; + + yargs.command(createCaris).parse('create-caris projectId tarFileId'); +}); + +test.serial.cb('The "create-foundation-bam" command should create a Foundation BAM ingestion', t => { + const res = { data: { id: 'ingestionId' } }; + postStub.onFirstCall().returns(res); + callback = () => { + t.is(postStub.callCount, 1); + t.is(postStub.getCall(0).args[1], '/v1/genomic-ingestion/projects/projectId/ingestions'); + t.deepEqual(postStub.getCall(0).args[2], { + ingestionType: 'FoundationBam', + inputFiles: { + bam: 'bamFileId' + }, + notificationConfig: { + succeededEmail: undefined, + failedEmail: undefined + } + }); + t.is(printSpy.callCount, 1); + t.true(printSpy.calledWith({ id: 'ingestionId' })); + t.end(); + }; + + yargs.command(createFoundationBam).parse('create-foundation-bam projectId bamFileId'); +}); + +test.serial.cb('The "create-caris-bam" command should create a Caris BAM ingestion', t => { + const res = { data: { id: 'ingestionId' } }; + postStub.onFirstCall().returns(res); + callback = () => { + t.is(postStub.callCount, 1); + t.is(postStub.getCall(0).args[1], '/v1/genomic-ingestion/projects/projectId/ingestions'); + t.deepEqual(postStub.getCall(0).args[2], { + ingestionType: 'CarisBam', + inputFiles: { + bam: 'bamFileId' + }, + notificationConfig: { + succeededEmail: undefined, + failedEmail: undefined + } + }); + t.is(printSpy.callCount, 1); + t.true(printSpy.calledWith({ id: 'ingestionId' })); + t.end(); + }; + + yargs.command(createCarisBam).parse('create-caris-bam projectId bamFileId'); +}); From ab81db4367a12747f025160ff4497e02334eb631 Mon Sep 17 00:00:00 2001 From: Connor Luebbehusen Date: Wed, 4 Jan 2023 10:24:15 -0500 Subject: [PATCH 4/4] docs: PHC-3861 - update changelog --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e37a020..4085752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [13.9.0] - 2023-01-04 + +### Added +- Added a `lo genomics ingestions` command set with the following commands + - `lo genomics ingestions list` Lists genomic ingestions for a project + - `lo genomics ingestions get` Gets a genomic ingestions + - `lo genomics ingestions create-foundation` Creates a Foundation ingestion + - `lo genomics ingestions create-caris` Creates a Caris ingestion + - `lo genomics ingestions create-foundation-bam` Creates a Foundation BAM ingestion + - `lo genomics ingestions create-caris-bam` Creates a Caris BAM ingestion + +## [13.8.1] - 2022-07-25 + +### Fixed +- Added `maxBodyLength` for file uploads + +## [13.8.0] - 2022-05-24 + +### Changed +- `lo surveys export-responses` now supports an optional `query` parameter + +## [13.7.0] - 2022-04-06 + +### Changed +- Updated how project is published + ## [13.6.0] - 2021-10-06 ### Changed @@ -891,6 +917,10 @@ and `create-nantomics-vcf-import` - Replaced the `defaults` command with a `setup` command +[13.9.0]: https://github.com/lifeomic/cli/compare/v13.8.1..v13.9.0 +[13.8.1]: https://github.com/lifeomic/cli/compare/v13.8.0..v13.8.1 +[13.8.0]: https://github.com/lifeomic/cli/compare/v13.7.0..v13.8.0 +[13.7.0]: https://github.com/lifeomic/cli/compare/v13.6.0..v13.7.0 [13.6.0]: https://github.com/lifeomic/cli/compare/v13.5.0..v13.6.0 [13.5.0]: https://github.com/lifeomic/cli/compare/v13.4.0..v13.5.0 [13.4.0]: https://github.com/lifeomic/cli/compare/v13.3.0..v13.4.0