bio-basespace-sdk 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bio-basespace-sdk might be problematic. Click here for more details.
- data/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +16 -0
- data/License.txt +275 -0
- data/README.md +671 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/examples/0_app_triggering.rb +135 -0
- data/examples/1_authentication.rb +156 -0
- data/examples/2_browsing.rb +84 -0
- data/examples/3_accessing_files.rb +129 -0
- data/examples/4_app_result_upload.rb +102 -0
- data/examples/5_purchasing.rb +119 -0
- data/lib/basespace.rb +126 -0
- data/lib/basespace/api/api_client.rb +313 -0
- data/lib/basespace/api/base_api.rb +242 -0
- data/lib/basespace/api/basespace_api.rb +789 -0
- data/lib/basespace/api/basespace_error.rb +80 -0
- data/lib/basespace/api/billing_api.rb +115 -0
- data/lib/basespace/model.rb +78 -0
- data/lib/basespace/model/app_result.rb +158 -0
- data/lib/basespace/model/app_result_response.rb +40 -0
- data/lib/basespace/model/app_session.rb +99 -0
- data/lib/basespace/model/app_session_compact.rb +43 -0
- data/lib/basespace/model/app_session_launch_object.rb +58 -0
- data/lib/basespace/model/app_session_response.rb +41 -0
- data/lib/basespace/model/application.rb +47 -0
- data/lib/basespace/model/application_compact.rb +44 -0
- data/lib/basespace/model/basespace_model.rb +60 -0
- data/lib/basespace/model/coverage.rb +48 -0
- data/lib/basespace/model/coverage_meta_response.rb +40 -0
- data/lib/basespace/model/coverage_metadata.rb +43 -0
- data/lib/basespace/model/coverage_response.rb +40 -0
- data/lib/basespace/model/file.rb +172 -0
- data/lib/basespace/model/file_response.rb +40 -0
- data/lib/basespace/model/genome_response.rb +40 -0
- data/lib/basespace/model/genome_v1.rb +56 -0
- data/lib/basespace/model/list_response.rb +53 -0
- data/lib/basespace/model/multipart_upload.rb +288 -0
- data/lib/basespace/model/product.rb +50 -0
- data/lib/basespace/model/project.rb +103 -0
- data/lib/basespace/model/project_response.rb +40 -0
- data/lib/basespace/model/purchase.rb +89 -0
- data/lib/basespace/model/purchase_response.rb +39 -0
- data/lib/basespace/model/purchased_product.rb +56 -0
- data/lib/basespace/model/query_parameters.rb +86 -0
- data/lib/basespace/model/query_parameters_purchased_product.rb +67 -0
- data/lib/basespace/model/refund_purchase_response.rb +40 -0
- data/lib/basespace/model/resource_list.rb +48 -0
- data/lib/basespace/model/response_status.rb +42 -0
- data/lib/basespace/model/run_compact.rb +55 -0
- data/lib/basespace/model/sample.rb +127 -0
- data/lib/basespace/model/sample_response.rb +40 -0
- data/lib/basespace/model/user.rb +80 -0
- data/lib/basespace/model/user_compact.rb +45 -0
- data/lib/basespace/model/user_response.rb +40 -0
- data/lib/basespace/model/variant.rb +57 -0
- data/lib/basespace/model/variant_header.rb +44 -0
- data/lib/basespace/model/variant_info.rb +48 -0
- data/lib/basespace/model/variants_header_response.rb +40 -0
- data/spec/basespaceapi_spec.rb +26 -0
- data/spec/basespaceerror_spec.rb +87 -0
- data/spec/basespacemodel_spec.rb +57 -0
- metadata +239 -0
@@ -0,0 +1,242 @@
|
|
1
|
+
# Copyright 2013 Toshiaki Katayama, Joachim Baran
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
require 'basespace/api/api_client'
|
15
|
+
require 'basespace/api/basespace_error'
|
16
|
+
|
17
|
+
require 'net/https'
|
18
|
+
require 'uri'
|
19
|
+
|
20
|
+
Net::HTTP.version_1_2
|
21
|
+
|
22
|
+
module Bio
|
23
|
+
module BaseSpace
|
24
|
+
|
25
|
+
# Parent class for BaseSpaceAPI and BillingAPI objects. It provides rudimentary
|
26
|
+
# API access functionality.
|
27
|
+
class BaseAPI
|
28
|
+
|
29
|
+
# Create a new BaseAPI instance using a given access token.
|
30
|
+
#
|
31
|
+
# +access_token+:: Access token provided by App triggering.
|
32
|
+
def initialize(access_token = nil)
|
33
|
+
if $DEBUG
|
34
|
+
$stderr.puts " # ----- BaseAPI#initialize ----- "
|
35
|
+
$stderr.puts " # caller: #{caller}"
|
36
|
+
$stderr.puts " # access_token: #{access_token}"
|
37
|
+
$stderr.puts " # "
|
38
|
+
end
|
39
|
+
@api_client = nil
|
40
|
+
set_timeout(10)
|
41
|
+
set_access_token(access_token) # logic for setting the access-token
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set a new access token. The value of a previously supplied access token
|
45
|
+
# is simply overwritten here.
|
46
|
+
#
|
47
|
+
# +access_token+:: Access token provided by App triggering.
|
48
|
+
def update_access_token(access_token)
|
49
|
+
@api_client.api_key = access_token
|
50
|
+
end
|
51
|
+
|
52
|
+
# Make a single request to the BaseSpace REST API.
|
53
|
+
#
|
54
|
+
# +my_model+:: Class or classname of the model that is applied in deserialization.
|
55
|
+
# +resource_path+:: URI that should be used for the API call.
|
56
|
+
# +method+:: HTTP method for the rest call (GET, POST, DELETE, etc.)
|
57
|
+
# +query_params+:: query parameters that should be sent along to the API.
|
58
|
+
# +header_params+:: Additional settings that should be transferred in the HTTP header.
|
59
|
+
# +post_data+:: Hash that contains data to be transferred.
|
60
|
+
# +verbose+:: Truth value indicating whether verbose output should be provided.
|
61
|
+
# +force_post+:: Truth value that indicates whether a POST should be forced.
|
62
|
+
# +no_api+:: (unclear; TODO)
|
63
|
+
def single_request(my_model, resource_path, method, query_params, header_params, post_data = nil, verbose = false, force_post = false, no_api = true)
|
64
|
+
# test if access-token has been set
|
65
|
+
if not @api_client and no_api
|
66
|
+
raise 'Access-token not set, use the "set_access_token"-method to supply a token value'
|
67
|
+
end
|
68
|
+
# Make the API Call
|
69
|
+
if verbose or $DEBUG
|
70
|
+
$stderr.puts " # ----- BaseAPI#single_request ----- "
|
71
|
+
$stderr.puts " # caller: #{caller}"
|
72
|
+
$stderr.puts " # resource_path: #{resource_path}"
|
73
|
+
$stderr.puts " # method: #{method}"
|
74
|
+
$stderr.puts " # query_params: #{query_params}"
|
75
|
+
$stderr.puts " # post_data: #{post_data}"
|
76
|
+
$stderr.puts " # header_params: #{header_params}"
|
77
|
+
$stderr.puts " # force_post: #{force_post}"
|
78
|
+
$stderr.puts " # "
|
79
|
+
end
|
80
|
+
response = @api_client.call_api(resource_path, method, query_params, post_data, header_params, force_post)
|
81
|
+
if verbose or $DEBUG
|
82
|
+
$stderr.puts " # ----- BaseAPI#single_request ----- "
|
83
|
+
$stderr.puts " # response: #{response.inspect}"
|
84
|
+
$stderr.puts " # "
|
85
|
+
end
|
86
|
+
unless response
|
87
|
+
raise 'BaseSpace error: None response returned'
|
88
|
+
end
|
89
|
+
|
90
|
+
# throw exception here for various error messages
|
91
|
+
if response['ResponseStatus'].has_key?('ErrorCode')
|
92
|
+
raise "BaseSpace error: #{response['ResponseStatus']['ErrorCode']}: #{response['ResponseStatus']['Message']}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# Create output objects if the response has more than one object
|
96
|
+
response_object = @api_client.deserialize(response, my_model)
|
97
|
+
return response_object.response
|
98
|
+
end
|
99
|
+
|
100
|
+
# Send a list request to the BaseSpace REST API.
|
101
|
+
#
|
102
|
+
# +my_model+:: Class or classname of the model that is applied in deserialization.
|
103
|
+
# +resource_path+:: URI that should be used for the API call.
|
104
|
+
# +method+:: HTTP method for the rest call (GET, POST, DELETE, etc.)
|
105
|
+
# +query_params+:: query parameters that should be sent along to the API.
|
106
|
+
# +header_params+:: Additional settings that should be transferred in the HTTP header.
|
107
|
+
# +verbose+:: Truth value indicating whether verbose output should be provided.
|
108
|
+
# +no_api+:: (unclear; TODO)
|
109
|
+
def list_request(my_model, resource_path, method, query_params, header_params, verbose = false, no_api = true)
|
110
|
+
# test if access-token has been set
|
111
|
+
if not @api_client and no_api
|
112
|
+
raise 'Access-token not set, use the "set_access_token"-method to supply a token value'
|
113
|
+
end
|
114
|
+
|
115
|
+
# Make the API Call
|
116
|
+
if verbose or $DEBUG
|
117
|
+
$stderr.puts " # ----- BaseAPI#list_request ----- "
|
118
|
+
$stderr.puts " # caller: #{caller}"
|
119
|
+
$stderr.puts " # Path: #{resource_path}"
|
120
|
+
$stderr.puts " # Pars: #{query_params}"
|
121
|
+
$stderr.puts " # "
|
122
|
+
end
|
123
|
+
response = @api_client.call_api(resource_path, method, query_params, nil, header_params) # post_data = nil
|
124
|
+
if verbose or $DEBUG
|
125
|
+
$stderr.puts " # ----- BaseAPI#list_request ----- "
|
126
|
+
$stderr.puts " # response: #{response.inspect}"
|
127
|
+
$stderr.puts " # "
|
128
|
+
end
|
129
|
+
unless response
|
130
|
+
raise "BaseSpace Exception: No data returned"
|
131
|
+
end
|
132
|
+
unless response.kind_of?(Array) # list
|
133
|
+
response = [response]
|
134
|
+
end
|
135
|
+
response_objects = []
|
136
|
+
response.each do |response_object|
|
137
|
+
response_objects << @api_client.deserialize(response_object, 'ListResponse')
|
138
|
+
end
|
139
|
+
|
140
|
+
# convert list response dict to object type
|
141
|
+
# TODO check that Response is present -- errors sometime don't include
|
142
|
+
# [TODO] check why the Python SDK only uses the first element in the response_objects
|
143
|
+
convertet = []
|
144
|
+
if response_object = response_objects.first
|
145
|
+
response_object.convert_to_object_list.each do |c|
|
146
|
+
convertet << @api_client.deserialize(c, my_model)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
return convertet
|
150
|
+
end
|
151
|
+
|
152
|
+
# URL encode a Hash of data values.
|
153
|
+
#
|
154
|
+
# +hash+:: data encoded in a Hash.
|
155
|
+
def hash2urlencode(hash)
|
156
|
+
# URI.escape (alias URI.encode) is obsolete since Ruby 1.9.2.
|
157
|
+
#return hash.map{|k,v| URI.encode(k.to_s) + "=" + URI.encode(v.to_s)}.join("&")
|
158
|
+
return hash.map{|k,v| URI.encode_www_form_component(k.to_s) + "=" + URI.encode_www_form_component(v.to_s)}.join("&")
|
159
|
+
end
|
160
|
+
|
161
|
+
# Post data to the given BaseSpace API URL. Method name is a bit of a
|
162
|
+
# misnomer, because 'curl' is not used by this method -- instead only
|
163
|
+
# Ruby core classes are used.
|
164
|
+
#
|
165
|
+
# +data+:: Data that should be transferred to the API.
|
166
|
+
# +url+:: URL of the BaseSpace API.
|
167
|
+
def make_curl_request(data, url)
|
168
|
+
if $DEBUG
|
169
|
+
$stderr.puts " # ----- BaseAPI#make_curl_request ----- "
|
170
|
+
$stderr.puts " # caller: #{caller}"
|
171
|
+
$stderr.puts " # data: #{data}"
|
172
|
+
$stderr.puts " # url: #{url}"
|
173
|
+
$stderr.puts " # "
|
174
|
+
end
|
175
|
+
post = hash2urlencode(data)
|
176
|
+
uri = URI.parse(url)
|
177
|
+
#res = Net::HTTP.post_form(uri, post).body
|
178
|
+
http_opts = {}
|
179
|
+
if uri.scheme == "https"
|
180
|
+
http_opts[:use_ssl] = true
|
181
|
+
end
|
182
|
+
res = Net::HTTP.start(uri.host, uri.port, http_opts) { |http|
|
183
|
+
http.post(uri.path, post)
|
184
|
+
}
|
185
|
+
obj = JSON.parse(res.body)
|
186
|
+
if $DEBUG
|
187
|
+
$stderr.puts " # res: #{res}"
|
188
|
+
$stderr.puts " # obj: #{obj}"
|
189
|
+
$stderr.puts " # "
|
190
|
+
end
|
191
|
+
if obj.has_key?('error')
|
192
|
+
raise "BaseSpace exception: " + obj['error'] + " - " + obj['error_description']
|
193
|
+
end
|
194
|
+
return obj
|
195
|
+
end
|
196
|
+
|
197
|
+
# Return a string representation of this object.
|
198
|
+
def to_s
|
199
|
+
return "BaseSpaceAPI instance - using token=#{get_access_token}"
|
200
|
+
end
|
201
|
+
|
202
|
+
# Return a string representation of this object.
|
203
|
+
def to_str
|
204
|
+
return self.to_s
|
205
|
+
end
|
206
|
+
|
207
|
+
# Specify the timeout in seconds for each request.
|
208
|
+
#
|
209
|
+
# +param time+:: Timeout in second.
|
210
|
+
def set_timeout(time)
|
211
|
+
@timeout = time
|
212
|
+
if @api_client
|
213
|
+
@api_client.timeout = @timeout
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Sets a new API token.
|
218
|
+
#
|
219
|
+
# +token+:: New API token.
|
220
|
+
def set_access_token(token)
|
221
|
+
@api_client = nil
|
222
|
+
if token
|
223
|
+
@api_client = APIClient.new(token, @api_server, @timeout)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Returns the access-token that was used to initialize the BaseSpaceAPI object.
|
228
|
+
def get_access_token
|
229
|
+
if @api_client
|
230
|
+
return @api_client.api_key
|
231
|
+
end
|
232
|
+
return "" # [TODO] Should return nil in Ruby?
|
233
|
+
end
|
234
|
+
|
235
|
+
# Returns the server URI used by this instance.
|
236
|
+
def get_server_uri
|
237
|
+
return @api_client.api_server
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
end # module BaseSpace
|
242
|
+
end # module Bio
|
@@ -0,0 +1,789 @@
|
|
1
|
+
# Copyright 2013 Toshiaki Katayama, Joachim Baran
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7
|
+
#
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11
|
+
# See the License for the specific language governing permissions and
|
12
|
+
# limitations under the License.
|
13
|
+
|
14
|
+
require 'basespace/api/api_client'
|
15
|
+
require 'basespace/api/base_api'
|
16
|
+
require 'basespace/api/basespace_error'
|
17
|
+
require 'basespace/model/query_parameters'
|
18
|
+
|
19
|
+
require 'net/https'
|
20
|
+
require 'uri'
|
21
|
+
require 'json'
|
22
|
+
|
23
|
+
Net::HTTP.version_1_2
|
24
|
+
|
25
|
+
module Bio
|
26
|
+
module BaseSpace
|
27
|
+
|
28
|
+
# The main API class used for all communication with the BaseSpace REST server.
|
29
|
+
class BaseSpaceAPI < BaseAPI
|
30
|
+
|
31
|
+
# URIs for obtaining a access token, user verification code, and app trigger information.
|
32
|
+
TOKEN_URL = '/oauthv2/token'
|
33
|
+
DEVICE_URL = "/oauthv2/deviceauthorization"
|
34
|
+
WEB_AUTHORIZE = '/oauth/authorize'
|
35
|
+
|
36
|
+
attr_reader :app_session_id
|
37
|
+
|
38
|
+
# Load credentials and start a new BaseSpace session.
|
39
|
+
def self.start
|
40
|
+
if opts = Bio::BaseSpace.load_credentials
|
41
|
+
self.new(opts['client_id'], opts['client_secret'], opts['basespace_url'], opts['api_version'], opts['app_session_id'], opts['access_token'])
|
42
|
+
else
|
43
|
+
raise "Please specify your BaseSpace credentials in the credentials.json file or use Bio::BaseSpace::BaseSpaceAPI.new with arguments"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a new object for communicating with the BaseSpace REST server; preferred method of calling
|
48
|
+
# is through the 'start' class method.
|
49
|
+
#
|
50
|
+
# +client_key+:: Client key to use for authentication (provided when registering an App).
|
51
|
+
# +client_secret+:: Client secret key to use for authentication (provided when registering an App).
|
52
|
+
# +api_server+:: URI of the BaseSpace API server.
|
53
|
+
# +version+:: API version to use.
|
54
|
+
# +app_session_id+:: App session ID that was generated by application triggering.
|
55
|
+
# +access_token+:: Access token provided by App triggering.
|
56
|
+
def initialize(client_key, client_secret, api_server, version, app_session_id = nil, access_token = nil)
|
57
|
+
end_with_slash = %r(/$)
|
58
|
+
unless api_server[end_with_slash]
|
59
|
+
api_server += '/'
|
60
|
+
end
|
61
|
+
|
62
|
+
@app_session_id = app_session_id
|
63
|
+
@key = client_key
|
64
|
+
@secret = client_secret
|
65
|
+
@api_server = api_server + version
|
66
|
+
@version = version
|
67
|
+
@weburl = api_server.sub('api.', '')
|
68
|
+
@timeout = nil
|
69
|
+
|
70
|
+
super(access_token)
|
71
|
+
end
|
72
|
+
|
73
|
+
# This method is not for general use and should only be called from 'get_app_session'.
|
74
|
+
#
|
75
|
+
# +obj+:: Application trigger JSON.
|
76
|
+
def get_trigger_object(obj)
|
77
|
+
if obj['ResponseStatus'].has_key?('ErrorCode')
|
78
|
+
raise 'BaseSpace error: ' + obj['ResponseStatus']['ErrorCode'].to_s + ": " + obj['ResponseStatus']['Message']
|
79
|
+
end
|
80
|
+
#access_token = nil # '' is false in Python but APIClient.new only raises when the value is None (not '')
|
81
|
+
access_token = ''
|
82
|
+
temp_api = APIClient.new(access_token, @api_server)
|
83
|
+
response = temp_api.deserialize(obj, 'AppSessionResponse')
|
84
|
+
# AppSessionResponse object has a response method which returns a AppSession object
|
85
|
+
app_sess = response.get_attr('Response')
|
86
|
+
# AppSession object has a serialize_references method which converts an array of
|
87
|
+
# AppSessionLaunchObject objects by calling serialize_object method in each object.
|
88
|
+
# The method in turn calls the serialize_object method of the given BaseSpaceAPI object
|
89
|
+
# with @content ('dict') and @type ('str') arguments. Returns an array of serialized objects.
|
90
|
+
res = app_sess.serialize_references(self)
|
91
|
+
return res
|
92
|
+
end
|
93
|
+
|
94
|
+
def serialize_object(d, type)
|
95
|
+
# [TODO] None (nil) or '' ?
|
96
|
+
#access_token = nil
|
97
|
+
access_token = ''
|
98
|
+
temp_api = APIClient.new(access_token, @api_server)
|
99
|
+
if type.downcase == 'project'
|
100
|
+
return temp_api.deserialize(d, 'Project')
|
101
|
+
end
|
102
|
+
if type.downcase == 'sample'
|
103
|
+
return temp_api.deserialize(d, 'Sample')
|
104
|
+
end
|
105
|
+
if type.downcase == 'appresult'
|
106
|
+
return temp_api.deserialize(d, 'AppResult')
|
107
|
+
end
|
108
|
+
return d
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# Returns the AppSession instance identified by the given ID.
|
113
|
+
#
|
114
|
+
# +id+:: The ID of the AppSession.
|
115
|
+
def get_app_session_by_id(id)
|
116
|
+
# TO_DO make special case for access-token only retrieval
|
117
|
+
return get_app_session(id)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns an AppSession instance containing user and data-type the app was triggered by/on.
|
121
|
+
#
|
122
|
+
# +id+:: The AppSessionId, ID not supplied the AppSessionId used for instantiating the BaseSpaceAPI instance.
|
123
|
+
def get_app_session(id = nil)
|
124
|
+
if (not @app_session_id) and (not id)
|
125
|
+
raise "This BaseSpaceAPI instance has no app_session_id set and no alternative id was supplied for method get_app_session"
|
126
|
+
end
|
127
|
+
|
128
|
+
# if (not id) and (not @key)
|
129
|
+
# raise "This BaseSpaceAPI instance has no client_secret (key) set and no alternative id was supplied for method get_app_session"
|
130
|
+
# end
|
131
|
+
|
132
|
+
resource_path = @api_server + '/appsessions/{AppSessionId}'
|
133
|
+
if id
|
134
|
+
resource_path = resource_path.sub('{AppSessionId}', id)
|
135
|
+
else
|
136
|
+
resource_path = resource_path.sub('{AppSessionId}', @app_session_id)
|
137
|
+
end
|
138
|
+
if $DEBUG
|
139
|
+
$stderr.puts " # ----- BaseSpaceAPI#get_app_session ----- "
|
140
|
+
$stderr.puts " # resource_path: #{resource_path}"
|
141
|
+
$stderr.puts " # "
|
142
|
+
end
|
143
|
+
uri = URI.parse(resource_path)
|
144
|
+
uri.user = @key
|
145
|
+
uri.password = @secret
|
146
|
+
#response = Net::HTTP.get(uri)
|
147
|
+
http_opts = {}
|
148
|
+
if uri.scheme == "https"
|
149
|
+
http_opts[:use_ssl] = true
|
150
|
+
end
|
151
|
+
response = Net::HTTP.start(uri.host, uri.port, http_opts) { |http|
|
152
|
+
request = Net::HTTP::Get.new(uri.path)
|
153
|
+
request.basic_auth uri.user, uri.password
|
154
|
+
http.request(request)
|
155
|
+
}
|
156
|
+
obj = JSON.parse(response.body)
|
157
|
+
# TODO add exception if response isn't OK, e.g. incorrect server gives path not recognized
|
158
|
+
return get_trigger_object(obj)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Request access to a data object.
|
162
|
+
#
|
163
|
+
# +obj+:: The data object we wish to get access to.
|
164
|
+
# +access_type+:: The type of access (read|write), default is write.
|
165
|
+
# +web+:: True if the App is web-based, default is false meaning a device based App.
|
166
|
+
# +redirect_url+:: For the web-based case, a redirection URL.
|
167
|
+
# +state+:: (unclear from Python port)
|
168
|
+
def get_access(obj, access_type = 'write', web = nil, redirect_url = nil, state = nil)
|
169
|
+
scope_str = obj.get_access_str(access_type)
|
170
|
+
if web
|
171
|
+
return get_web_verification_code(scope_str, redirect_url, state)
|
172
|
+
else
|
173
|
+
return get_verification_code(scope_str)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns the BaseSpace dictionary containing the verification code and verification URL for the user to approve
|
178
|
+
# access to a specific data scope.
|
179
|
+
#
|
180
|
+
# Corresponding curl call:
|
181
|
+
# curlCall = 'curl -d "response_type=device_code" -d "client_id=' + client_key + '" -d "scope=' + scope + '" ' + DEVICE_URL
|
182
|
+
#
|
183
|
+
# For details see:
|
184
|
+
# https://developer.basespace.illumina.com/docs/content/documentation/authentication/obtaining-access-tokens
|
185
|
+
#
|
186
|
+
# +scope+:: The scope that access is requested for.
|
187
|
+
def get_verification_code(scope)
|
188
|
+
#curlCall = 'curl -d "response_type=device_code" -d "client_id=' + @key + '" -d "scope=' + scope + '" ' + @api_server + DEVICE_URL
|
189
|
+
#puts curlCall
|
190
|
+
unless @key
|
191
|
+
raise "This BaseSpaceAPI instance has no client_secret (key) set and no alternative id was supplied for method get_verification_code"
|
192
|
+
end
|
193
|
+
data = {'client_id' => @key, 'scope' => scope, 'response_type' => 'device_code'}
|
194
|
+
return make_curl_request(data, @api_server + DEVICE_URL)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Generates the URL the user should be redirected to for web-based authentication.
|
198
|
+
#
|
199
|
+
# +scope+: The scope that access is requested for.
|
200
|
+
# +redirect_url+:: The redirect URL.
|
201
|
+
# +state+:: An optional state parameter that will passed through to the redirect response.
|
202
|
+
def get_web_verification_code(scope, redirect_url, state = nil)
|
203
|
+
if (not @key)
|
204
|
+
raise "This BaseSpaceAPI instance has no client_id (key) set and no alternative id was supplied for method get_verification_code"
|
205
|
+
end
|
206
|
+
data = {'client_id' => @key, 'redirect_uri' => redirect_url, 'scope' => scope, 'response_type' => 'code', "state" => state}
|
207
|
+
return @weburl + WEB_AUTHORIZE + '?' + hash2urlencode(data)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns a user specific access token.
|
211
|
+
#
|
212
|
+
# +device_code+:: The device code returned by the verification code method.
|
213
|
+
def obtain_access_token(device_code)
|
214
|
+
if (not @key) or (not @secret)
|
215
|
+
raise "This BaseSpaceAPI instance has either no client_secret or no client_id set and no alternative id was supplied for method get_verification_code"
|
216
|
+
end
|
217
|
+
data = {'client_id' => @key, 'client_secret' => @secret, 'code' => device_code, 'grant_type' => 'device', 'redirect_uri' => 'google.com'}
|
218
|
+
dict = make_curl_request(data, @api_server + TOKEN_URL)
|
219
|
+
return dict['access_token']
|
220
|
+
end
|
221
|
+
|
222
|
+
def update_privileges(code)
|
223
|
+
token = obtain_access_token(code)
|
224
|
+
set_access_token(token)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Creates a project with the specified name and returns a project object.
|
228
|
+
# If a project with this name already exists, the existing project is returned.
|
229
|
+
#
|
230
|
+
# +name+:: Name of the project.
|
231
|
+
def create_project(name)
|
232
|
+
#: v1pre3/projects, it requires 1 input parameter which is Name
|
233
|
+
my_model = 'ProjectResponse'
|
234
|
+
resource_path = '/projects/'
|
235
|
+
resource_path = resource_path.sub('{format}', 'json')
|
236
|
+
method = 'POST'
|
237
|
+
query_params = {}
|
238
|
+
header_params = {}
|
239
|
+
post_data = {}
|
240
|
+
post_data['Name'] = name
|
241
|
+
verbose = false
|
242
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns the User object corresponding to ID.
|
246
|
+
#
|
247
|
+
# +id+:: The ID of the user.
|
248
|
+
def get_user_by_id(id)
|
249
|
+
my_model = 'UserResponse'
|
250
|
+
resource_path = '/users/{Id}'
|
251
|
+
resource_path = resource_path.sub('{format}', 'json')
|
252
|
+
resource_path = resource_path.sub('{Id}', id)
|
253
|
+
method = 'GET'
|
254
|
+
query_params = {}
|
255
|
+
header_params = {}
|
256
|
+
return single_request(my_model, resource_path, method, query_params, header_params)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns an AppResult object corresponding to ID.
|
260
|
+
#
|
261
|
+
# +param id+:: The ID of the AppResult.
|
262
|
+
def get_app_result_by_id(id)
|
263
|
+
my_model = 'AppResultResponse'
|
264
|
+
resource_path = '/appresults/{Id}'
|
265
|
+
resource_path = resource_path.sub('{format}', 'json')
|
266
|
+
resource_path = resource_path.sub('{Id}', id)
|
267
|
+
method = 'GET'
|
268
|
+
query_params = {}
|
269
|
+
header_params = {}
|
270
|
+
return single_request(my_model, resource_path, method, query_params, header_params)
|
271
|
+
end
|
272
|
+
|
273
|
+
# Returns a list of File objects for the AppResult with the given ID.
|
274
|
+
#
|
275
|
+
# +id+:: The ID of the AppResult.
|
276
|
+
# +qp: An object of type QueryParameters for custom sorting and filtering.
|
277
|
+
def get_app_result_files(id, qp = {})
|
278
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
279
|
+
query_pars.validate
|
280
|
+
my_model = 'File'
|
281
|
+
resource_path = '/appresults/{Id}/files'
|
282
|
+
resource_path = resource_path.sub('{format}', 'json')
|
283
|
+
resource_path = resource_path.sub('{Id}', id)
|
284
|
+
method = 'GET'
|
285
|
+
query_params = query_pars.get_parameter_dict
|
286
|
+
header_params = {}
|
287
|
+
verbose = false
|
288
|
+
return list_request(my_model, resource_path, method, query_params, header_params, verbose)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Request a project object by ID.
|
292
|
+
#
|
293
|
+
# +id+:: The ID of the project.
|
294
|
+
def get_project_by_id(id)
|
295
|
+
my_model = 'ProjectResponse'
|
296
|
+
resource_path = '/projects/{Id}'
|
297
|
+
resource_path = resource_path.sub('{format}', 'json')
|
298
|
+
resource_path = resource_path.sub('{Id}', id)
|
299
|
+
method = 'GET'
|
300
|
+
query_params = {}
|
301
|
+
header_params = {}
|
302
|
+
return single_request(my_model, resource_path, method, query_params, header_params)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Returns a list available projects for a User with the specified ID.
|
306
|
+
#
|
307
|
+
# +id+:: The ID of the user.
|
308
|
+
# +qp+:: An object of type QueryParameters for custom sorting and filtering.
|
309
|
+
def get_project_by_user(id, qp = {})
|
310
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
311
|
+
query_pars.validate
|
312
|
+
my_model = 'Project'
|
313
|
+
resource_path = '/users/{Id}/projects'
|
314
|
+
resource_path = resource_path.sub('{format}', 'json')
|
315
|
+
resource_path = resource_path.sub('{Id}', id) if id != nil
|
316
|
+
method = 'GET'
|
317
|
+
query_params = query_pars.get_parameter_dict
|
318
|
+
header_params = {}
|
319
|
+
return list_request(my_model, resource_path, method, query_params, header_params)
|
320
|
+
end
|
321
|
+
|
322
|
+
# Returns a list of accessible runs for the User with the given ID.
|
323
|
+
#
|
324
|
+
# +id+:: User id.
|
325
|
+
# +qp+:: An object of type QueryParameters for custom sorting and filtering.
|
326
|
+
def get_accessible_runs_by_user(id, qp = {})
|
327
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
328
|
+
query_pars.validate
|
329
|
+
my_model = 'RunCompact'
|
330
|
+
resource_path = '/users/{Id}/runs'
|
331
|
+
resource_path = resource_path.sub('{format}', 'json')
|
332
|
+
resource_path = resource_path.sub('{Id}', id)
|
333
|
+
method = 'GET'
|
334
|
+
query_params = query_pars.get_parameter_dict
|
335
|
+
header_params = {}
|
336
|
+
return list_request(my_model, resource_path, method, query_params, header_params)
|
337
|
+
end
|
338
|
+
|
339
|
+
# Returns a list of AppResult object associated with the project with ID.
|
340
|
+
#
|
341
|
+
# +id+:: The project ID.
|
342
|
+
# +qp+:: An object of type QueryParameters for custom sorting and filtering.
|
343
|
+
# +statuses+:: A list of AppResult statuses to filter by.
|
344
|
+
def get_app_results_by_project(id, qp = {}, statuses = [])
|
345
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
346
|
+
query_pars.validate
|
347
|
+
my_model = 'AppResult'
|
348
|
+
resource_path = '/projects/{Id}/appresults'
|
349
|
+
resource_path = resource_path.sub('{format}', 'json')
|
350
|
+
resource_path = resource_path.sub('{Id}', id)
|
351
|
+
method = 'GET'
|
352
|
+
query_params = query_pars.get_parameter_dict
|
353
|
+
unless statuses.empty?
|
354
|
+
query_params['Statuses'] = statuses.join(",")
|
355
|
+
end
|
356
|
+
header_params = {}
|
357
|
+
verbose = false
|
358
|
+
return list_request(my_model, resource_path, method, query_params, header_params, verbose)
|
359
|
+
end
|
360
|
+
|
361
|
+
# Returns a list of samples associated with a project with ID.
|
362
|
+
#
|
363
|
+
# +id+:: The ID of the project.
|
364
|
+
# +qp+:: An object of type QueryParameters for custom sorting and filtering.
|
365
|
+
def get_samples_by_project(id, qp = {})
|
366
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
367
|
+
query_pars.validate
|
368
|
+
my_model = 'Sample'
|
369
|
+
resource_path = '/projects/{Id}/samples'
|
370
|
+
resource_path = resource_path.sub('{format}', 'json')
|
371
|
+
resource_path = resource_path.sub('{Id}', id)
|
372
|
+
method = 'GET'
|
373
|
+
query_params = query_pars.get_parameter_dict
|
374
|
+
header_params = {}
|
375
|
+
verbose = false
|
376
|
+
return list_request(my_model, resource_path, method, query_params, header_params, verbose)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Returns a Sample object.
|
380
|
+
#
|
381
|
+
# +id+:: The ID of the sample.
|
382
|
+
def get_sample_by_id(id)
|
383
|
+
my_model = 'SampleResponse'
|
384
|
+
resource_path = '/samples/{Id}'
|
385
|
+
resource_path = resource_path.sub('{format}', 'json')
|
386
|
+
resource_path = resource_path.sub('{Id}', id)
|
387
|
+
method = 'GET'
|
388
|
+
query_params = {}
|
389
|
+
header_params = {}
|
390
|
+
post_data = nil
|
391
|
+
verbose = false
|
392
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
393
|
+
end
|
394
|
+
|
395
|
+
# Returns a list of File objects associated with sample with ID.
|
396
|
+
#
|
397
|
+
# +id+:: Sample ID.
|
398
|
+
# +qp+:: An object of type QueryParameters for custom sorting and filtering.
|
399
|
+
def get_files_by_sample(id, qp = {})
|
400
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
401
|
+
query_pars.validate
|
402
|
+
my_model = 'File'
|
403
|
+
resource_path = '/samples/{Id}/files'
|
404
|
+
resource_path = resource_path.sub('{format}', 'json')
|
405
|
+
resource_path = resource_path.sub('{Id}', id)
|
406
|
+
method = 'GET'
|
407
|
+
query_params = query_pars.get_parameter_dict
|
408
|
+
header_params = {}
|
409
|
+
verbose = false
|
410
|
+
return list_request(my_model, resource_path, method, query_params, header_params, verbose)
|
411
|
+
end
|
412
|
+
|
413
|
+
# Returns a file object by ID.
|
414
|
+
#
|
415
|
+
# +id+:: The ID of the file.
|
416
|
+
def get_file_by_id(id)
|
417
|
+
my_model = 'FileResponse'
|
418
|
+
resource_path = '/files/{Id}'
|
419
|
+
resource_path = resource_path.sub('{format}', 'json')
|
420
|
+
resource_path = resource_path.sub('{Id}', id)
|
421
|
+
method = 'GET'
|
422
|
+
query_params = {}
|
423
|
+
header_params = {}
|
424
|
+
post_data = nil
|
425
|
+
verbose = false
|
426
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
427
|
+
end
|
428
|
+
|
429
|
+
# Returns an instance of Genome with the specified ID.
|
430
|
+
#
|
431
|
+
# +id+:: The genome ID.
|
432
|
+
def get_genome_by_id(id)
|
433
|
+
my_model = 'GenomeResponse'
|
434
|
+
resource_path = '/genomes/{Id}'
|
435
|
+
resource_path = resource_path.sub('{format}', 'json')
|
436
|
+
resource_path = resource_path.sub('{Id}', id)
|
437
|
+
method = 'GET'
|
438
|
+
query_params = {}
|
439
|
+
header_params = {}
|
440
|
+
return single_request(my_model, resource_path, method, query_params, header_params)
|
441
|
+
end
|
442
|
+
|
443
|
+
# Returns a list of all available genomes.
|
444
|
+
#
|
445
|
+
# +qp+:: An object of type QueryParameters for custom sorting and filtering.
|
446
|
+
def get_available_genomes(qp = {})
|
447
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
448
|
+
query_pars.validate
|
449
|
+
my_model = 'GenomeV1'
|
450
|
+
resource_path = '/genomes'
|
451
|
+
resource_path = resource_path.sub('{format}', 'json')
|
452
|
+
method = 'GET'
|
453
|
+
query_params = query_pars.get_parameter_dict
|
454
|
+
header_params = {}
|
455
|
+
verbose = false
|
456
|
+
return list_request(my_model, resource_path, method, query_params, header_params, verbose)
|
457
|
+
end
|
458
|
+
|
459
|
+
# TODO, needs more work in parsing meta data, currently only map keys are returned.
|
460
|
+
|
461
|
+
# Returns a VariantMetadata object for a variant file.
|
462
|
+
#
|
463
|
+
# +id+:: ID of the VCF file.
|
464
|
+
# +format+:: Set to 'vcf' to get the results as lines in VCF format.
|
465
|
+
def get_variant_metadata(id, format)
|
466
|
+
my_model = 'VariantsHeaderResponse'
|
467
|
+
resource_path = '/variantset/{Id}'
|
468
|
+
resource_path = resource_path.sub('{format}', 'json')
|
469
|
+
resource_path = resource_path.sub('{Id}', id)
|
470
|
+
method = 'GET'
|
471
|
+
query_params = {}
|
472
|
+
query_params['Format'] = @api_client.to_path_value(format)
|
473
|
+
header_params = {}
|
474
|
+
verbose = false
|
475
|
+
return single_request(my_model, resource_path, method, query_params, header_params, verbose)
|
476
|
+
end
|
477
|
+
|
478
|
+
# List the variants in a set of variants. Maximum returned records is 1000.
|
479
|
+
#
|
480
|
+
# +id+:: ID of the variant file.
|
481
|
+
# +chrom+:: The chromosome of interest.
|
482
|
+
# +start_pos+:: The start position of the sequence of interest.
|
483
|
+
# +end_pos+:: The start position of the sequence of interest.
|
484
|
+
# +format+:: Set to 'vcf' to get the results as lines in VCF format.
|
485
|
+
# +qp+:: An (optional) object of type QueryParameters for custom sorting and filtering.
|
486
|
+
def filter_variant_set(id, chrom, start_pos, end_pos, format, qp = {'SortBy' => 'Position'})
|
487
|
+
query_pars = qp.kind_of?(Hash) ? QueryParameters.new(qp) : qp
|
488
|
+
query_pars.validate
|
489
|
+
my_model = 'Variant'
|
490
|
+
resource_path = '/variantset/{Id}/variants/chr{Chrom}'
|
491
|
+
resource_path = resource_path.sub('{format}', 'json')
|
492
|
+
resource_path = resource_path.sub('{Chrom}', chrom)
|
493
|
+
resource_path = resource_path.sub('{Id}', id)
|
494
|
+
method = 'GET'
|
495
|
+
query_params = query_pars.get_parameter_dict
|
496
|
+
header_params = {}
|
497
|
+
query_params['StartPos'] = start_pos
|
498
|
+
query_params['EndPos'] = end_pos
|
499
|
+
query_params['Format'] = format
|
500
|
+
verbose = false
|
501
|
+
return list_request(my_model, resource_path, method, query_params, header_params, verbose)
|
502
|
+
end
|
503
|
+
|
504
|
+
# Mean coverage levels over a sequence interval. Returns an instance of CoverageResponse.
|
505
|
+
#
|
506
|
+
# +id+:: Chromosome to query.
|
507
|
+
# +chrom+:: The ID of the resource.
|
508
|
+
# +start_pos+:: Get coverage starting at this position. Default is 1.
|
509
|
+
# +end_pos+:: Get coverage up to and including this position. Default is start_pos + 1280.
|
510
|
+
def get_interval_coverage(id, chrom, start_pos = nil, end_pos = nil)
|
511
|
+
my_model = 'CoverageResponse'
|
512
|
+
resource_path = '/coverage/{Id}/{Chrom}'
|
513
|
+
resource_path = resource_path.sub('{format}', 'json')
|
514
|
+
resource_path = resource_path.sub('{Chrom}', chrom)
|
515
|
+
resource_path = resource_path.sub('{Id}', id)
|
516
|
+
method = 'GET'
|
517
|
+
query_params = {}
|
518
|
+
header_params = {}
|
519
|
+
query_params['StartPos'] = @api_client.to_path_value(start_pos)
|
520
|
+
query_params['EndPos'] = @api_client.to_path_value(end_pos)
|
521
|
+
post_data = nil
|
522
|
+
verbose = false
|
523
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
524
|
+
end
|
525
|
+
|
526
|
+
# Returns Metadata about coverage as a CoverageMetadata instance.
|
527
|
+
#
|
528
|
+
# +id+:: ID of a BAM file.
|
529
|
+
# +chrom+:: Chromosome to query.
|
530
|
+
def get_coverage_meta_info(id, chrom)
|
531
|
+
my_model = 'CoverageMetaResponse'
|
532
|
+
resource_path = '/coverage/{Id}/{Chrom}/meta'
|
533
|
+
resource_path = resource_path.sub('{format}', 'json')
|
534
|
+
resource_path = resource_path.sub('{Chrom}', chrom)
|
535
|
+
resource_path = resource_path.sub('{Id}', id)
|
536
|
+
method = 'GET'
|
537
|
+
query_params = {}
|
538
|
+
header_params = {}
|
539
|
+
post_data = nil
|
540
|
+
verbose = false
|
541
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
542
|
+
end
|
543
|
+
|
544
|
+
# Create an AppResult object.
|
545
|
+
#
|
546
|
+
# +id+:: ID of the project in which the AppResult is to be added.
|
547
|
+
# +name+:: The name of the AppResult.
|
548
|
+
# +desc+:: A describtion of the AppResult.
|
549
|
+
# +samples+:: List of samples (if any).
|
550
|
+
# +app_session_id+:: If no app_session_id is given, the id used to initialize the BaseSpaceAPI instance will be used. If app_session_id is set equal to an empty string, a new appsession will be created.
|
551
|
+
def create_app_result(id, name, desc, samples = [], app_session_id = nil)
|
552
|
+
if (not @app_session_id) and (not app_session_id)
|
553
|
+
raise "This BaseSpaceAPI instance has no app_session_id set and no alternative id was supplied for method create_app_result"
|
554
|
+
end
|
555
|
+
|
556
|
+
my_model = 'AppResultResponse'
|
557
|
+
resource_path = '/projects/{ProjectId}/appresults'
|
558
|
+
resource_path = resource_path.sub('{format}', 'json')
|
559
|
+
resource_path = resource_path.sub('{ProjectId}', id)
|
560
|
+
method = 'POST'
|
561
|
+
query_params = {}
|
562
|
+
header_params = {}
|
563
|
+
post_data = {}
|
564
|
+
verbose = false
|
565
|
+
|
566
|
+
if app_session_id
|
567
|
+
query_params['appsessionid'] = app_session_id
|
568
|
+
else
|
569
|
+
query_params['appsessionid'] = @app_session_id # default case, we use the current appsession
|
570
|
+
end
|
571
|
+
|
572
|
+
# add the sample references
|
573
|
+
if samples.length > 0
|
574
|
+
ref = []
|
575
|
+
samples.each do |s|
|
576
|
+
d = { "Rel" => "using", "Type" => "Sample", "HrefContent" => @version + '/samples/' + s.id }
|
577
|
+
ref << d
|
578
|
+
end
|
579
|
+
post_data['References'] = ref
|
580
|
+
end
|
581
|
+
|
582
|
+
# case, an appSession is provided, we need to check if the a
|
583
|
+
if query_params.has_key?('appsessionid')
|
584
|
+
sid = query_params['appsessionid']
|
585
|
+
session = get_app_session(sid)
|
586
|
+
unless session.can_work_on
|
587
|
+
raise 'AppSession status must be "running," to create and AppResults. Current status is ' + session.status
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
post_data['Name'] = name
|
592
|
+
post_data['Description'] = desc
|
593
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
594
|
+
end
|
595
|
+
|
596
|
+
# Uploads a file associated with an AppResult to BaseSpace and returns the corresponding file object.
|
597
|
+
#
|
598
|
+
# +id+:: AppResult ID.
|
599
|
+
# +local_path+:: The local path to the file to be uploaded.
|
600
|
+
# +file_name+:: The desired filename in the AppResult folder on the BaseSpace server.
|
601
|
+
# +directory+:: The directory the file should be placed in.
|
602
|
+
# +content_type+:: The content-type of the file.
|
603
|
+
def app_result_file_upload(id, local_path, file_name, directory, content_type, multipart = 0)
|
604
|
+
my_model = 'FileResponse'
|
605
|
+
resource_path = '/appresults/{Id}/files'
|
606
|
+
resource_path = resource_path.sub('{format}', 'json')
|
607
|
+
resource_path = resource_path.sub('{Id}', id)
|
608
|
+
method = 'POST'
|
609
|
+
query_params = {}
|
610
|
+
header_params = {}
|
611
|
+
verbose = false
|
612
|
+
|
613
|
+
query_params['name'] = file_name
|
614
|
+
query_params['directory'] = directory
|
615
|
+
header_params['Content-Type'] = content_type
|
616
|
+
|
617
|
+
# three cases, two for multipart, starting
|
618
|
+
if multipart == 1
|
619
|
+
query_params['multipart'] = 'true'
|
620
|
+
post_data = nil
|
621
|
+
force_post = true
|
622
|
+
# Set force post as this need to use POST though no data is being streamed
|
623
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose, force_post)
|
624
|
+
elsif multipart == 2
|
625
|
+
query_params = {'uploadstatus' => 'complete'}
|
626
|
+
post_data = nil
|
627
|
+
force_post = true
|
628
|
+
# Set force post as this need to use POST though no data is being streamed
|
629
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose, force_post)
|
630
|
+
else
|
631
|
+
post_data = File.open(local_path).read
|
632
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
636
|
+
# Downloads a BaseSpace file to a local directory.
|
637
|
+
#
|
638
|
+
# +id+:: File ID.
|
639
|
+
# +local_dir+:: The local directory to place the file in.
|
640
|
+
# +name+:: The name of the local file.
|
641
|
+
# +range+:: The byte range of the file to retrieve (not yet implemented).
|
642
|
+
def file_download(id, local_dir, name, range = []) #@ReservedAssignment
|
643
|
+
resource_path = '/files/{Id}/content'
|
644
|
+
resource_path = resource_path.sub('{format}', 'json')
|
645
|
+
resource_path = resource_path.sub('{Id}', id)
|
646
|
+
method = 'GET'
|
647
|
+
query_params = {}
|
648
|
+
header_params = {}
|
649
|
+
|
650
|
+
query_params['redirect'] = 'meta' # we need to add this parameter to get the Amazon link directly
|
651
|
+
|
652
|
+
response = @api_client.call_api(resource_path, method, query_params, nil, header_params)
|
653
|
+
if response['ResponseStatus'].has_key?('ErrorCode')
|
654
|
+
raise 'BaseSpace error: ' + response['ResponseStatus']['ErrorCode'].to_s + ": " + response['ResponseStatus']['Message']
|
655
|
+
end
|
656
|
+
|
657
|
+
# get the Amazon URL
|
658
|
+
file_url = response['Response']['HrefContent']
|
659
|
+
|
660
|
+
header = nil
|
661
|
+
unless range.empty?
|
662
|
+
# puts "Case range request"
|
663
|
+
header = { 'Range' => format('bytes=%s-%s', range[0], range[1]) }
|
664
|
+
end
|
665
|
+
|
666
|
+
# Do the download
|
667
|
+
File.open(File.join(local_dir, name), "wb") do |fp|
|
668
|
+
http_opts = {}
|
669
|
+
if uri.scheme == "https"
|
670
|
+
http_opts[:use_ssl] = true
|
671
|
+
end
|
672
|
+
uri = URI.parse(file_url)
|
673
|
+
res = Net::HTTP.start(uri.host, uri.port, http_opts) { |http|
|
674
|
+
# [TODO] Do we need user and pass here also?
|
675
|
+
http.get(uri.path, header)
|
676
|
+
}
|
677
|
+
fp.print res.body
|
678
|
+
end
|
679
|
+
|
680
|
+
return 1
|
681
|
+
end
|
682
|
+
|
683
|
+
# Returns URL of a file on S3.
|
684
|
+
#
|
685
|
+
# +id+:: File ID.
|
686
|
+
def file_url(id) # @ReservedAssignment
|
687
|
+
resource_path = '/files/{Id}/content'
|
688
|
+
resource_path = resource_path.sub('{format}', 'json')
|
689
|
+
resource_path = resource_path.sub('{Id}', id)
|
690
|
+
method = 'GET'
|
691
|
+
query_params = {}
|
692
|
+
header_params = {}
|
693
|
+
|
694
|
+
query_params['redirect'] = 'meta' # we need to add this parameter to get the Amazon link directly
|
695
|
+
|
696
|
+
response = @api_client.call_api(resource_path, method, query_params, nil, header_params)
|
697
|
+
if response['ResponseStatus'].has_key?('ErrorCode')
|
698
|
+
raise 'BaseSpace error: ' + response['ResponseStatus']['ErrorCode'].to_s + ": " + response['ResponseStatus']['Message']
|
699
|
+
end
|
700
|
+
|
701
|
+
# return the Amazon URL
|
702
|
+
return response['Response']['HrefContent']
|
703
|
+
end
|
704
|
+
|
705
|
+
|
706
|
+
# Helper method for uploading multipart files, do not call directly.
|
707
|
+
#
|
708
|
+
# +id+:: File ID.
|
709
|
+
# +part_number+:: File part to be uploaded.
|
710
|
+
# +md5+:: MD5 sum of datastream.
|
711
|
+
# +data+:: The data-stream to be uploaded.
|
712
|
+
def upload_multipart_unit(id, part_number, md5, data)
|
713
|
+
resource_path = '/files/{Id}/parts/{partNumber}'
|
714
|
+
resource_path = resource_path.sub('{format}', 'json')
|
715
|
+
resource_path = resource_path.sub('{Id}', id)
|
716
|
+
resource_path = resource_path.sub('{partNumber}', part_number.to_s)
|
717
|
+
method = 'PUT'
|
718
|
+
query_params = {}
|
719
|
+
header_params = {'Content-MD5' => md5.strip()}
|
720
|
+
force_post = false
|
721
|
+
out = @api_client.call_api(resource_path, method, query_params, data, header_params, force_post)
|
722
|
+
return out
|
723
|
+
# curl -v -H "x-access-token: {access token}" \
|
724
|
+
# -H "Content-MD5: 9mvo6qaA+FL1sbsIn1tnTg==" \
|
725
|
+
# -T reportarchive.zipaa \
|
726
|
+
# -X PUT https://api.cloud-endor.illumina.com/v1pre2/files/7094087/parts/1
|
727
|
+
end
|
728
|
+
|
729
|
+
# Not yet implemented (by Illumina Python SDK)
|
730
|
+
#
|
731
|
+
# def large_file_download
|
732
|
+
# raise 'Not yet implemented'
|
733
|
+
# end
|
734
|
+
|
735
|
+
# Method for multi-threaded file-upload for parallel transfer of very large files (currently only runs on unix systems)
|
736
|
+
#
|
737
|
+
#
|
738
|
+
# :param id: The AppResult ID
|
739
|
+
# :param local_path: The local path of the file to be uploaded
|
740
|
+
# :param file_name: The desired filename on the server
|
741
|
+
# :param directory: The server directory to place the file in (empty string will place it in the root directory)
|
742
|
+
# :param content_type: The content type of the file
|
743
|
+
# :param tempdir: Temp directory to use, if blank the directory for 'local_path' will be used
|
744
|
+
# :param cpuCount: The number of CPUs to be used
|
745
|
+
# :param partSize: The size of individual upload parts (must be between 5 and 25mb)
|
746
|
+
# :param verbose: Write process output to stdout as upload progresses
|
747
|
+
#
|
748
|
+
# def multipart_file_upload(self, id, local_path, file_name, directory, content_type, tempdir = nil, cpuCount = 2, partSize = 25, verbose = false)
|
749
|
+
# # Create file object on server
|
750
|
+
# multipart = 1
|
751
|
+
# my_file = app_result_file_upload(id, local_path, file_name, directory, content_type, multipart)
|
752
|
+
#
|
753
|
+
# # prepare multi-par upload objects
|
754
|
+
# my_mpu = mpu(self, id, local_path, my_file, cpu_count, part_size, tempdir, verbose)
|
755
|
+
# return my_mpu
|
756
|
+
# end
|
757
|
+
#
|
758
|
+
# def mark_file_state(id)
|
759
|
+
# end
|
760
|
+
|
761
|
+
# Set the status of an AppResult object.
|
762
|
+
#
|
763
|
+
# +id+:: The id of the AppResult.
|
764
|
+
# +status+:: Status assignment string.
|
765
|
+
# +summary+:: Summary string.
|
766
|
+
def set_app_session_state(id, status, summary)
|
767
|
+
my_model = 'AppSessionResponse'
|
768
|
+
resource_path = '/appsessions/{Id}'
|
769
|
+
resource_path = resource_path.sub('{format}', 'json')
|
770
|
+
resource_path = resource_path.sub('{Id}', id)
|
771
|
+
method = 'POST'
|
772
|
+
query_params = {}
|
773
|
+
header_params = {}
|
774
|
+
post_data = {}
|
775
|
+
verbose = false
|
776
|
+
|
777
|
+
status_allowed = ['running', 'complete', 'needsattention', 'aborted', 'error']
|
778
|
+
unless status_allowed.include?(status.downcase)
|
779
|
+
raise "AppResult state must be in #{status_allowed.inspect}"
|
780
|
+
end
|
781
|
+
post_data['status'] = status.downcase
|
782
|
+
post_data['statussummary'] = summary
|
783
|
+
return single_request(my_model, resource_path, method, query_params, header_params, post_data, verbose)
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
end # module BaseSpace
|
788
|
+
end # module Bio
|
789
|
+
|