solvebio 1.5.2 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +13 -8
- data/Gemfile +4 -2
- data/README.md +5 -3
- data/demo/cheatsheet.rb +31 -0
- data/lib/cli/auth.rb +6 -6
- data/lib/cli/irbrc.rb +2 -1
- data/lib/cli/options.rb +1 -1
- data/lib/client.rb +85 -83
- data/lib/credentials.rb +2 -2
- data/lib/main.rb +11 -2
- data/lib/query.rb +5 -6
- data/lib/resource/annotation.rb +23 -0
- data/lib/resource/apiresource.rb +241 -0
- data/lib/resource/dataset.rb +91 -0
- data/lib/resource/datasetfield.rb +37 -0
- data/lib/resource/depository.rb +50 -0
- data/lib/resource/depositoryversion.rb +69 -0
- data/lib/resource/main.rb +123 -0
- data/lib/resource/sample.rb +75 -0
- data/lib/{solveobject.rb → resource/solveobject.rb} +43 -22
- data/lib/resource/user.rb +5 -0
- data/lib/solvebio.rb +1 -1
- data/lib/util.rb +29 -0
- data/solvebio.gemspec +7 -4
- data/test/Makefile +9 -0
- data/test/data/sample.vcf.gz +0 -0
- data/test/helper.rb +9 -2
- data/test/test-annotation.rb +46 -0
- data/test/test-auth.rb +8 -4
- data/test/test-client.rb +6 -6
- data/test/test-conversion.rb +13 -0
- data/test/test-dataset.rb +42 -0
- data/test/test-depository.rb +35 -0
- data/test/test-netrc.rb +13 -3
- data/test/test-query-batch.rb +26 -46
- data/test/test-query-paging.rb +77 -98
- data/test/test-query.rb +47 -64
- data/test/test-resource.rb +8 -15
- data/test/test-sample-access.rb +59 -0
- data/test/test-sample-download.rb +20 -0
- data/test/test-tabulate.rb +27 -23
- data/test/{test-solveobject.rb → test-util.rb} +17 -2
- metadata +128 -56
- data/lib/apiresource.rb +0 -130
- data/lib/help.rb +0 -46
- data/lib/resource.rb +0 -414
data/.travis.yml
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
before_script:
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
- gem install netrc --version=0.7.7
|
4
|
+
- gem install rest_client addressable
|
5
|
+
- touch ~/.netrc
|
6
|
+
- chmod 0600 ~/.netrc
|
6
7
|
env:
|
7
|
-
|
8
|
+
global:
|
9
|
+
- COLUMNS=80
|
8
10
|
script: rake
|
9
11
|
rvm:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
- 1.9.3
|
13
|
+
- 2.0.0
|
14
|
+
- 2.1.1
|
15
|
+
- 2.1.2
|
16
|
+
notifications:
|
17
|
+
slack:
|
18
|
+
secure: aIpZOAZwIk/3ZToZqAPEdM4yKyD6UuDmR+KyH2eL+24mTxU+Jp8hum02LlRsdeOXweup9Ov/h7SuDQXfEt506oLmkhfDwk+v2j2RkHmFco4l4M+rI1QAtJw1xhzrscwgnJqkW8n81U5Se84KzU+x7N6EvDcki8G1KlbuU2A9f74=
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -10,13 +10,15 @@ For more information about SolveBio, see https://www.solvebio.com
|
|
10
10
|
|
11
11
|
# Installation
|
12
12
|
|
13
|
-
|
13
|
+
gem install solvebio # may need sudo
|
14
|
+
|
15
|
+
Installing from git:
|
14
16
|
|
15
17
|
git clone https://github.com/solvebio/solvebio-ruby.git
|
16
18
|
cd solvebio-ruby
|
17
19
|
gem install netrc # install gem dependencies, may need sudo
|
18
|
-
rake test # or make test
|
19
|
-
sudo rake install # or make install
|
20
|
+
rake test # or "make test"
|
21
|
+
sudo rake install # or "make install"
|
20
22
|
|
21
23
|
This also builds a *solvebio* gem which you can use elsewhere.
|
22
24
|
|
data/demo/cheatsheet.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Cheet sheet for SolveBio API
|
2
|
+
require 'solvebio'
|
3
|
+
|
4
|
+
# Load the Dataset object
|
5
|
+
dataset = SolveBio::Dataset.retrieve('ClinVar/2.0.0-1/Variants')
|
6
|
+
|
7
|
+
# Print the Dataset
|
8
|
+
puts dataset.query()
|
9
|
+
|
10
|
+
# Get help (fields/facets)
|
11
|
+
dataset.help()
|
12
|
+
|
13
|
+
# Query the dataset (filterless)
|
14
|
+
q = dataset.query()
|
15
|
+
|
16
|
+
filters = SolveBio::Filter.new :gene_symbols => "BRCA2"
|
17
|
+
|
18
|
+
puts dataset.query(:filters => filters)
|
19
|
+
|
20
|
+
# Multiple keyword filter (boolean 'or')
|
21
|
+
filters = SolveBio::Filter.new :gene_symbols => "BRCA2"
|
22
|
+
filters |= SolveBio::Filter.new :gene_symbols => "BRCA1"
|
23
|
+
|
24
|
+
# Same as above 'or' in one go using 'in'
|
25
|
+
filters = SolveBio::Filter.new :gene_symbols__in => ["BRCA2", "BRCA1"]
|
26
|
+
|
27
|
+
puts dataset.query(:filters => filters)
|
28
|
+
|
29
|
+
# Range filter. Like 'in' for a contiguous numeric range
|
30
|
+
dataset.query(filters =>
|
31
|
+
SolveBio::RangeFilter.new('hg38', "13", 32_200_000, 32_200_500))
|
data/lib/cli/auth.rb
CHANGED
@@ -37,9 +37,9 @@ module SolveBio::Auth
|
|
37
37
|
:architecture => SolveBio::ARCHITECTURE,
|
38
38
|
# :processor => processor(),
|
39
39
|
}
|
40
|
-
SolveBio::Client.client
|
41
|
-
|
42
|
-
|
40
|
+
SolveBio::Client.client
|
41
|
+
.request('post', '/v1/reports/install',
|
42
|
+
{:params => data}) rescue nil
|
43
43
|
end
|
44
44
|
|
45
45
|
|
@@ -51,7 +51,7 @@ module SolveBio::Auth
|
|
51
51
|
# Prompt user for login information (email/password).
|
52
52
|
# Email and password are used to get the user's auth_token key.
|
53
53
|
#
|
54
|
-
def login(email=nil
|
54
|
+
def login(email=nil)
|
55
55
|
delete_credentials
|
56
56
|
|
57
57
|
email, password = ask_for_credentials email unless
|
@@ -65,8 +65,8 @@ module SolveBio::Auth
|
|
65
65
|
# code. Not sure if it's valid here, or what the equivalent
|
66
66
|
# is.
|
67
67
|
begin
|
68
|
-
response = SolveBio::Client.
|
69
|
-
|
68
|
+
response = SolveBio::Client.client
|
69
|
+
.request 'post', '/v1/auth/token', {:payload => data}
|
70
70
|
rescue SolveBio::Error => e
|
71
71
|
puts "Login failed: #{e.to_s}"
|
72
72
|
return false
|
data/lib/cli/irbrc.rb
CHANGED
@@ -10,6 +10,7 @@ IRB.conf[:PROMPT][:SIMPLE] = {
|
|
10
10
|
}
|
11
11
|
|
12
12
|
require_relative '../solvebio'
|
13
|
+
require_relative '../resource/apiresource'
|
13
14
|
include SolveBio::Auth
|
14
15
|
|
15
16
|
# Set some demo names that can be used.
|
@@ -48,6 +49,6 @@ creds = get_credentials()
|
|
48
49
|
if creds
|
49
50
|
puts "You are logged in as #{creds[0]}"
|
50
51
|
else
|
51
|
-
puts 'You are not logged in yet. Login using "login [email
|
52
|
+
puts 'You are not logged in yet. Login using "login [email]"'
|
52
53
|
|
53
54
|
end
|
data/lib/cli/options.rb
CHANGED
@@ -46,7 +46,7 @@ SolveBio Commands:
|
|
46
46
|
login [email] Login and save credentials. Use email if provided.
|
47
47
|
logout Logout and delete saved credentials
|
48
48
|
whoami Show your SolveBio email address
|
49
|
-
shell Open
|
49
|
+
shell Open a SolveBio IRB shell
|
50
50
|
test Make sure the SolveBio API is working correctly
|
51
51
|
EOH
|
52
52
|
exit
|
data/lib/client.rb
CHANGED
@@ -1,30 +1,39 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# -*- coding: utf-8 -*-
|
3
3
|
require 'openssl'
|
4
|
-
require '
|
4
|
+
require 'rest_client'
|
5
5
|
require 'json'
|
6
|
+
require 'addressable/uri'
|
6
7
|
require_relative 'credentials'
|
7
8
|
require_relative 'errors'
|
8
9
|
|
9
|
-
# import textwrap
|
10
|
-
|
11
10
|
# A requests-based HTTP client for SolveBio API resources
|
12
11
|
class SolveBio::Client
|
13
12
|
|
14
13
|
attr_reader :headers, :api_host
|
15
14
|
attr_accessor :api_key
|
16
15
|
|
16
|
+
# Add our own kind of Authorization tokens. This has to be
|
17
|
+
# done this way, late, because the rest-client gem looks for
|
18
|
+
# .netrc and will set basic authentication if it finds a match.
|
19
|
+
RestClient.add_before_execution_proc do | req, args |
|
20
|
+
if args[:authorization]
|
21
|
+
req.instance_variable_get('@header')['authorization'] = [args[:authorization]]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
17
25
|
def initialize(api_key=nil, api_host=nil)
|
18
26
|
@api_key = api_key || SolveBio::api_key
|
19
27
|
SolveBio::api_key ||= api_key
|
20
28
|
@api_host = api_host || SolveBio::API_HOST
|
29
|
+
|
21
30
|
# Mirroring comments from:
|
22
31
|
# http://ruby-doc.org/stdlib-2.1.2/libdoc/net/http/rdoc/Net/HTTP.html
|
23
32
|
# gzip compression is used in preference to deflate
|
24
33
|
# compression, which is used in preference to no compression.
|
25
34
|
@headers = {
|
26
|
-
|
27
|
-
|
35
|
+
:content_type => :json,
|
36
|
+
:accept => :json,
|
28
37
|
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
|
29
38
|
'User-Agent' => 'SolveBio Ruby Client %s [%s/%s]' % [
|
30
39
|
SolveBio::VERSION,
|
@@ -34,119 +43,112 @@ class SolveBio::Client
|
|
34
43
|
}
|
35
44
|
end
|
36
45
|
|
37
|
-
|
46
|
+
DEFAULT_REQUEST_OPTS = {
|
47
|
+
:raw => false,
|
48
|
+
:default_headers => true
|
49
|
+
}
|
38
50
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
51
|
+
# Issues an HTTP GET across the wire via the Ruby 'rest-client'
|
52
|
+
# library. See *request()* for information on opts.
|
53
|
+
def get(url, opts={})
|
54
|
+
request('get', url, opts)
|
55
|
+
end
|
44
56
|
|
45
|
-
|
46
|
-
|
57
|
+
# Issues an HTTP POST across the wire via the Ruby 'rest-client'
|
58
|
+
# library. See *request* for information on opts.
|
59
|
+
def post(url, data, opts={})
|
60
|
+
opts[:payload] =
|
61
|
+
if opts.member?(:no_json)
|
62
|
+
data
|
63
|
+
else
|
64
|
+
data.to_json
|
65
|
+
end
|
66
|
+
request('post', url, opts)
|
67
|
+
end
|
47
68
|
|
48
|
-
|
49
|
-
|
69
|
+
# Issues an HTTP Request across the wire via the Ruby 'rest-client'
|
70
|
+
# library.
|
71
|
+
def request(method, url, opts={})
|
50
72
|
|
51
|
-
|
52
|
-
http.use_ssl = true
|
53
|
-
# FIXME? Risky - see
|
54
|
-
# http://www.rubyinside.com/how-to-cure-nethttps-risky-default-https-behavior-4010.html
|
55
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
56
|
-
end
|
73
|
+
opts = DEFAULT_REQUEST_OPTS.merge(opts)
|
57
74
|
|
58
|
-
|
59
|
-
|
75
|
+
# Expand URL with API host if none was given
|
76
|
+
api_host = @api_host or SolveBio::API_HOST
|
60
77
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
request = Net::HTTP::Post.new(uri.request_uri)
|
66
|
-
request.body = params.to_json
|
67
|
-
else
|
68
|
-
request = Net::HTTP::Get.new(uri.request_uri)
|
69
|
-
end
|
70
|
-
@headers.each { |k, v| request.add_field(k, v) }
|
71
|
-
request.add_field('Authorization', "Token #{@api_key}") if @api_key
|
72
|
-
response = http.request(request)
|
73
|
-
|
74
|
-
# FIXME: There's probably gzip decompression built in to
|
75
|
-
# net/http. Until I figure out how to get that to work, the
|
76
|
-
# below works.
|
77
|
-
case response
|
78
|
-
when Net::HTTPSuccess then
|
79
|
-
begin
|
80
|
-
if response['Content-Encoding'].eql?( 'gzip' ) then
|
81
|
-
puts "Performing gzip decompression for response body." if $DEBUG
|
82
|
-
sio = StringIO.new( response.body )
|
83
|
-
gz = Zlib::GzipReader.new( sio )
|
84
|
-
response.body = gz.read()
|
85
|
-
end
|
86
|
-
rescue Exception
|
87
|
-
puts "Error occurred (#{$!.message})" if $DEBUG
|
88
|
-
# handle errors
|
89
|
-
raise $!.message
|
90
|
-
end
|
78
|
+
if not api_host
|
79
|
+
raise SolveBio::Error.new('No SolveBio API host is set')
|
80
|
+
elsif not url.start_with?(api_host)
|
81
|
+
url = Addressable::URI.join(api_host, url).to_s
|
91
82
|
end
|
92
83
|
|
93
|
-
|
94
|
-
if
|
95
|
-
|
84
|
+
# Handle some default options and add authorization header
|
85
|
+
if opts[:default_headers] and @api_key
|
86
|
+
headers = @headers.merge(opts[:headers]||{})
|
87
|
+
authorization = "Token #{@api_key}"
|
88
|
+
else
|
89
|
+
headers = nil
|
90
|
+
authorization = nil
|
96
91
|
end
|
97
92
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
93
|
+
SolveBio::logger.debug('API %s Request: %s' % [method.upcase, url])
|
94
|
+
|
95
|
+
response = nil
|
96
|
+
RestClient::Request.
|
97
|
+
execute(:method => method,
|
98
|
+
:url => url,
|
99
|
+
:headers => headers,
|
100
|
+
:authorization => authorization,
|
101
|
+
:timeout => opts[:timeout] || 80,
|
102
|
+
:payload => opts[:payload]) do
|
103
|
+
|resp, request, result, &block|
|
104
|
+
response = resp
|
105
|
+
if response.code < 200 or response.code >= 400
|
106
|
+
self.handle_api_error(result)
|
107
|
+
end
|
102
108
|
end
|
109
|
+
|
110
|
+
response = JSON.parse(response) unless opts[:raw]
|
111
|
+
response
|
103
112
|
end
|
104
113
|
|
105
|
-
def handle_request_error(e)
|
114
|
+
def self.handle_request_error(e)
|
106
115
|
# FIXME: go over this. It is still a rough translation
|
107
116
|
# from the python.
|
108
117
|
err = e.inspect
|
109
118
|
if e.kind_of?(requests.exceptions.RequestException)
|
110
119
|
msg = SolveBio::Error::Default_message
|
111
120
|
else
|
112
|
-
msg =
|
121
|
+
msg = "Unexpected error communicating with SolveBio.\n" +
|
113
122
|
"It looks like there's probably a configuration " +
|
114
|
-
'issue locally
|
123
|
+
'issue locally.\nIf this problem persists, let us ' +
|
115
124
|
'know at contact@solvebio.com.'
|
116
125
|
end
|
117
126
|
msg = msg + "\n\n(Network error: #{err}"
|
118
127
|
raise SolveBio::Error.new(nil, msg)
|
119
128
|
end
|
120
129
|
|
130
|
+
# SolveBio's API error handler returns a SolveBio::Error. The
|
131
|
+
# *response* parameter is a (subclass) of Net::HTTPResponse.
|
121
132
|
def handle_api_error(response)
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
SolveBio::logger.info("API Error: #{response.msg}")
|
126
|
-
raise SolveBio::Error.new(response)
|
127
|
-
end
|
133
|
+
SolveBio::logger.info("API Error: #{response.msg}") unless
|
134
|
+
[400, 401, 403, 404].member?(response.code.to_i)
|
135
|
+
raise SolveBio::Error.new(response)
|
128
136
|
end
|
129
137
|
|
130
138
|
def self.client
|
131
139
|
@@client ||= SolveBio::Client.new()
|
132
140
|
end
|
133
141
|
|
142
|
+
def self.get(*args)
|
143
|
+
client.get(*args)
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.post(*args)
|
147
|
+
client.post(*args)
|
148
|
+
end
|
149
|
+
|
134
150
|
def self.request(*args)
|
135
151
|
client.request(*args)
|
136
152
|
end
|
137
153
|
|
138
154
|
end
|
139
|
-
|
140
|
-
if __FILE__ == $0
|
141
|
-
puts SolveBio::Client.client.headers
|
142
|
-
puts SolveBio::Client.client.api_host
|
143
|
-
client = SolveBio::Client.new(nil, 'http://google.com')
|
144
|
-
response = client.request('http', 'http://google.com') rescue 'no good'
|
145
|
-
puts response.inspect
|
146
|
-
puts '-' * 30
|
147
|
-
response = client.request('http', 'http://www.google.com') rescue 'nope'
|
148
|
-
puts response.inspect
|
149
|
-
puts '-' * 30
|
150
|
-
response = client.request('http', 'https://www.google.com') rescue 'nope'
|
151
|
-
puts response.inspect
|
152
|
-
end
|
data/lib/credentials.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Deals with reading netrc credentials
|
4
4
|
require_relative 'main'
|
5
5
|
require 'netrc'
|
6
|
-
require 'uri'
|
6
|
+
require 'addressable/uri'
|
7
7
|
|
8
8
|
#
|
9
9
|
# Raised if the credentials are not found.
|
@@ -17,7 +17,7 @@ module SolveBio::Credentials
|
|
17
17
|
|
18
18
|
# SolveBio API host -- just the hostname
|
19
19
|
def api_host
|
20
|
-
URI(SolveBio::API_HOST).host
|
20
|
+
Addressable::URI.parse(SolveBio::API_HOST).host
|
21
21
|
end
|
22
22
|
|
23
23
|
def netrc_path
|
data/lib/main.rb
CHANGED
@@ -7,12 +7,21 @@
|
|
7
7
|
# Have questions or comments? email us at: contact@solvebio.com
|
8
8
|
|
9
9
|
require 'logger'
|
10
|
+
require 'fileutils'
|
10
11
|
|
11
12
|
module SolveBio
|
12
13
|
|
13
|
-
VERSION = '1.
|
14
|
+
VERSION = '1.6.1'
|
14
15
|
@api_key = ENV['SOLVEBIO_API_KEY']
|
15
|
-
|
16
|
+
logfile =
|
17
|
+
if ENV['SOLVEBIO_LOGFILE']
|
18
|
+
ENV['SOLVEBIO_LOGFILE']
|
19
|
+
else
|
20
|
+
dir = File::expand_path '~/.solvebio'
|
21
|
+
FileUtils.mkdir_p(dir) unless File.exist? dir
|
22
|
+
File::expand_path File.join(dir, 'solvebio.log')
|
23
|
+
end
|
24
|
+
@logger = Logger.new(logfile)
|
16
25
|
API_HOST = ENV['SOLVEBIO_API_HOST'] || 'https://api.solvebio.com'
|
17
26
|
|
18
27
|
# Config info in reports and requests. Encapsulate more?
|
data/lib/query.rb
CHANGED
@@ -118,11 +118,11 @@ class SolveBio::PagingQuery
|
|
118
118
|
return 'query returned 0 results'
|
119
119
|
end
|
120
120
|
|
121
|
+
sorted_items = SolveBio::Tabulate.
|
122
|
+
tabulate(self[0].to_a.sort_by{|x| x[0]})
|
121
123
|
msg =
|
122
124
|
"\n%s\n\n... %s more results." %
|
123
|
-
[
|
124
|
-
['Fields', 'Data'],
|
125
|
-
['right', 'left']),
|
125
|
+
[sorted_items, ['Fields', 'Data'], ['right', 'left'],
|
126
126
|
(@total - 1).pretty_int]
|
127
127
|
return msg
|
128
128
|
end
|
@@ -283,7 +283,7 @@ class SolveBio::PagingQuery
|
|
283
283
|
_params.merge!(params)
|
284
284
|
SolveBio::logger.debug("querying dataset: #{_params}")
|
285
285
|
|
286
|
-
@response = SolveBio::Client.client.
|
286
|
+
@response = SolveBio::Client.client.post(@data_url, _params)
|
287
287
|
@total = @response['total']
|
288
288
|
SolveBio::logger.
|
289
289
|
debug("query response took: #{@response['took']} ms, " +
|
@@ -379,8 +379,7 @@ class SolveBio::BatchQuery
|
|
379
379
|
def execute(params={})
|
380
380
|
_params = build_query()
|
381
381
|
_params.merge!(params)
|
382
|
-
response = SolveBio::Client.
|
383
|
-
client.request('post', '/v1/batch_query', _params)
|
382
|
+
response = SolveBio::Client.client.post('/v1/batch_query', _params)
|
384
383
|
return response
|
385
384
|
end
|
386
385
|
end
|