solvebio 1.5.2 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.travis.yml +13 -8
  2. data/Gemfile +4 -2
  3. data/README.md +5 -3
  4. data/demo/cheatsheet.rb +31 -0
  5. data/lib/cli/auth.rb +6 -6
  6. data/lib/cli/irbrc.rb +2 -1
  7. data/lib/cli/options.rb +1 -1
  8. data/lib/client.rb +85 -83
  9. data/lib/credentials.rb +2 -2
  10. data/lib/main.rb +11 -2
  11. data/lib/query.rb +5 -6
  12. data/lib/resource/annotation.rb +23 -0
  13. data/lib/resource/apiresource.rb +241 -0
  14. data/lib/resource/dataset.rb +91 -0
  15. data/lib/resource/datasetfield.rb +37 -0
  16. data/lib/resource/depository.rb +50 -0
  17. data/lib/resource/depositoryversion.rb +69 -0
  18. data/lib/resource/main.rb +123 -0
  19. data/lib/resource/sample.rb +75 -0
  20. data/lib/{solveobject.rb → resource/solveobject.rb} +43 -22
  21. data/lib/resource/user.rb +5 -0
  22. data/lib/solvebio.rb +1 -1
  23. data/lib/util.rb +29 -0
  24. data/solvebio.gemspec +7 -4
  25. data/test/Makefile +9 -0
  26. data/test/data/sample.vcf.gz +0 -0
  27. data/test/helper.rb +9 -2
  28. data/test/test-annotation.rb +46 -0
  29. data/test/test-auth.rb +8 -4
  30. data/test/test-client.rb +6 -6
  31. data/test/test-conversion.rb +13 -0
  32. data/test/test-dataset.rb +42 -0
  33. data/test/test-depository.rb +35 -0
  34. data/test/test-netrc.rb +13 -3
  35. data/test/test-query-batch.rb +26 -46
  36. data/test/test-query-paging.rb +77 -98
  37. data/test/test-query.rb +47 -64
  38. data/test/test-resource.rb +8 -15
  39. data/test/test-sample-access.rb +59 -0
  40. data/test/test-sample-download.rb +20 -0
  41. data/test/test-tabulate.rb +27 -23
  42. data/test/{test-solveobject.rb → test-util.rb} +17 -2
  43. metadata +128 -56
  44. data/lib/apiresource.rb +0 -130
  45. data/lib/help.rb +0 -46
  46. data/lib/resource.rb +0 -414
data/.travis.yml CHANGED
@@ -1,13 +1,18 @@
1
1
  language: ruby
2
2
  before_script:
3
- - gem install netrc
4
- - touch ~/.netrc
5
- - chmod 0600 ~/.netrc
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
- - "COLUMNS=80"
8
+ global:
9
+ - COLUMNS=80
8
10
  script: rake
9
11
  rvm:
10
- - 1.9.3
11
- - 2.0.0
12
- - 2.1.1
13
- - 2.1.2
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
@@ -1,4 +1,6 @@
1
- # A sample Gemfile
1
+ # A bundler Gemfile for specifying dependencies. See http://bundler.io/
2
2
  source "https://rubygems.org"
3
3
 
4
- gem "netrc"
4
+ gem 'addressable'
5
+ gem 'rest_client'
6
+ gem 'netrc', '~>0.7.7' # rest_client needs netrc 0.7.7
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
- Right now we only support installing from git:
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
 
@@ -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.request('post',
41
- '/v1/reports/install',
42
- data) rescue nil
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, password=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
- client.request('post', '/v1/auth/token', data)
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 [, password]]"'
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 the SolveBio Python shell
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 'net/http'
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
- 'Content-Type' => 'application/json',
27
- 'Accept' => 'application/json',
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
- def request(method, url, params=nil, raw=false)
46
+ DEFAULT_REQUEST_OPTS = {
47
+ :raw => false,
48
+ :default_headers => true
49
+ }
38
50
 
39
- if not @api_host
40
- raise SolveBio::Error.new(nil, 'No SolveBio API host is set')
41
- elsif not url.start_with?(@api_host)
42
- url = URI.join(@api_host, url).to_s
43
- end
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
- uri = URI.parse(url)
46
- http = Net::HTTP.new(uri.host, uri.port)
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
- # Note: there's also read_timeout and ssl_timeout
49
- http.open_timeout = 80 # in seconds
69
+ # Issues an HTTP Request across the wire via the Ruby 'rest-client'
70
+ # library.
71
+ def request(method, url, opts={})
50
72
 
51
- if uri.scheme == 'https'
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
- http.set_debug_output($stderr) if $DEBUG
59
- SolveBio::logger.debug('API %s Request: %s' % [method.upcase, url])
75
+ # Expand URL with API host if none was given
76
+ api_host = @api_host or SolveBio::API_HOST
60
77
 
61
- request = nil
62
- if ['POST', 'PUT', 'PATCH'].member?(method.upcase)
63
- # FIXME? do we need to do something different for
64
- # PUT and PATCH?
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
- status_code = response.code.to_i
94
- if status_code < 200 or status_code >= 300
95
- handle_api_error(response)
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
- if raw
99
- return response.body
100
- else
101
- return JSON.parse(response.body)
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 = 'Unexpected error communicating with SolveBio. ' +
121
+ msg = "Unexpected error communicating with SolveBio.\n" +
113
122
  "It looks like there's probably a configuration " +
114
- 'issue locally. If this problem persists, let us ' +
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
- if [400, 401, 403, 404].member?(response.code.to_i)
123
- raise SolveBio::Error.new(response)
124
- else
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.5.0'
14
+ VERSION = '1.6.1'
14
15
  @api_key = ENV['SOLVEBIO_API_KEY']
15
- @logger = Logger.new('/tmp/solvebio.log')
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
- [SolveBio::Tabulate.tabulate(self[0].to_a,
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.request('post', @data_url, _params)
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