Skip to content

Commit

Permalink
First public commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mschroering committed Mar 23, 2018
0 parents commit 1f2b7ea
Show file tree
Hide file tree
Showing 46 changed files with 6,381 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,json,yml}]
charset = utf-8
indent_style = space
indent_size = 2
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# dotenv environment variables file
.env

.vscode

build/
work/
target/
.git/
node_modules/
test/integration/data/output
dist/
*.log
*.bak.js
*.bak
.DS_STORE
dump.rdb
test-results.xml

deploy/.npmrc
deploy/yarn.lock
deploy/terraform/run.zip
run.zip
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test
Jenkinsfile
publish-private.sh
79 changes: 79 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [3.1.0] - 2018-03-12

### Added

- Commands for task resources

## [2.5.1] - 2018-01-19

### Fixed

- The `lo fhir` `--limit` option will not set pageSize when requesting resources

## [2.5.0] - 2018-01-15

### Added

- Commands for FHIR resources

## [2.4.2] - 2018-01-12

### Fixed

- `lo auth -c` now produces an error message if a clip board utility isn't installed
- `lo auth` no longer hangs for 1 minute

## [2.4.1] - 2018-01-04

### Fixed

- Changed `files-download` to work with new download response.

## [2.4.0] - 2018-01-04

### Added

- Added support for GA4GH RNA Quantification sets.

## [2.3.1] - 2018-01-03

### Fixed

- Throw a proper error when `setup` has not been performed.

## [2.3.0] - 2018-01-02

### Changed

- The `auth` command was changed to perform authentication using the web login view.

## [2.2.0] - 2017-12-21

### Added

- Commands for GA4GH resources

## [2.1.0] - 2017-12-21

### Added

- Support to specify client credential settings in the `setup` command
- Ability to automatically refresh access tokens on expiration

## [2.0.0] - 2017-12-20

### Added

- Added a `--copy` option to the `auth` command to copy the access token to the
clipboard

### Changed

- Replaced the `defaults` command with a `setup` command
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2018 Phillip Gates-Idem

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# lifeomic-cli

This project is a small CLI app that provides functionality around the LifeOmic
API's.

## Installation

```bash
yarn global add @lifeomic/lifeomic-cli
```

Run `lo setup` to configure the default environment and account you wish to use.
You can later override the default account using the `-a` option for commands.
If using client credentials, you will also be given the opportunity to store the
client settings at this time.

Use `lo auth` to obtain access credentials when using username / password
authentication. This command currently only supports LifeOmic users and not
those that have been federated via social or SAML 2.0 identity providers.

## Usage

```bash
lo <command> [options]
```

View available commands:

```bash
lo --help
```

Get help for a command:

```bash
lo <command> --help
```
164 changes: 164 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
'use strict';

const axios = require('axios');
const axiosRetry = require('axios-retry');
const chalk = require('chalk');
const { Bar } = require('cli-progress');
const fs = require('fs');
const config = require('./config');
const mkdirp = require('mkdirp');
const { name, version } = require('../package.json');
const queue = require('queue');
const { dirname } = require('path');
const tokenProvider = require('./interceptor/tokenProvider');

axiosRetry(axios, { retries: 3 });
axios.defaults.headers.common['User-Agent'] = `${name}/${version}`;

const FIVE_MB = 5 * 1024 * 1024;
const MAX_PARTS = 10000;

function request (options) {
const environment = config.getEnvironment();

const account = options.account || config.get(`${environment}.defaults.account`);
if (!account) {
throw new Error(`Account needs to be set with 'lo defaults' or specified with the -a option.`);
}

const client = axios.create({
baseURL: config.get(`${environment}.apiUrl`),
headers: {
'LifeOmic-Account': account
}
});
client.interceptors.request.use(tokenProvider);
axiosRetry(client, { retries: 3 });
return client;
}

function progress (name) {
return new Bar({
format: `${name} [{bar}] {percentage}% | ETA: {eta}s | {value}/{total} bytes`,
hideCursor: true,
clearOnComplete: true,
barsize: 30
});
}

module.exports.MULTIPART_MIN_SIZE = FIVE_MB;

module.exports.get = function (options, path) {
return request(options).get(path);
};

module.exports.del = function (options, path) {
return request(options).delete(path);
};

module.exports.post = function (options, path, body) {
return request(options).post(path, body);
};

module.exports.download = async function (options, path, fileName) {
mkdirp(dirname(fileName));

const response = await request(options).get(path);

const bar = progress(fileName);

const res = await axios({
method: 'get',
url: response.data.downloadUrl,
responseType: 'stream'
});

bar.start(res.headers['content-length'], 0);
res.data
.on('data', e => {
bar.increment(e.length);
})
.on('end', () => {
bar.stop();
console.log(chalk.green(`Downloaded: ${fileName}`));
})
// eslint-disable-next-line security/detect-non-literal-fs-filename
.pipe(fs.createWriteStream(fileName));
};

module.exports.upload = async function (uploadUrl, fileName, fileSize) {
const bar = progress(fileName);
bar.start(fileSize, 0);

// eslint-disable-next-line security/detect-non-literal-fs-filename
const stream = fs.createReadStream(fileName)
.on('data', e => {
bar.increment(e.length);
})
.on('end', () => {
bar.stop();
});

await axios({
method: 'put',
url: uploadUrl,
data: stream,
headers: {
'Content-Length': fileSize
}
});
};

function startQueue (q) {
return new Promise((resolve, reject) => {
q.start(error => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}

module.exports.multipartUpload = async function (options, uploadId, fileName, fileSize) {
const bar = progress(fileName);
bar.start(fileSize, 0);
const q = queue({
concurrency: options.parallel || 4
});

let partSize = Math.ceil(fileSize / MAX_PARTS);
partSize = Math.max(partSize, FIVE_MB);
const totalParts = Math.ceil(fileSize / partSize);

for (let part = 1; part <= totalParts; ++part) {
q.push(async () => {
const start = (part - 1) * (partSize);
const end = part === totalParts ? fileSize - 1 : start + partSize - 1;
const size = end - start + 1;
// eslint-disable-next-line security/detect-non-literal-fs-filename
const stream = fs.createReadStream(fileName, {
start: start,
end: end
});

const response = await request(options).get(`/v1/uploads/${uploadId}/parts/${part}`);

await axios({
method: 'put',
url: response.data.uploadUrl,
data: stream,
headers: {
'Content-Length': size
}
});
bar.increment(size);
});
}

await startQueue(q);

bar.stop();
await request(options).delete(`/v1/uploads/${uploadId}`);
};
21 changes: 21 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const program = require('commander');
const glob = require('glob');
const path = require('path');
const version = require('../package.json').version;

exports.run = () => {
program
.version(version)
.arguments('<command>')
.usage('<command> [options]');

const commands = glob.sync(path.join(__dirname, 'commands/*.js'));
for (const command of commands) {
// eslint-disable-next-line security/detect-non-literal-require
require(command);
}

program.parse(process.argv);
};
Loading

0 comments on commit 1f2b7ea

Please sign in to comment.