bio-basespace-sdk 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
|
+
|