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,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2013 Toshiaki Katayama, Joachim Baran
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
# Creating an AppResult and uploading files
|
17
|
+
# https://developer.basespace.illumina.com/docs/content/documentation/sdk-samples/python-sdk-overview#Creating_an_AppResult_and_uploading_files
|
18
|
+
|
19
|
+
require 'basespace'
|
20
|
+
|
21
|
+
include Bio::BaseSpace
|
22
|
+
|
23
|
+
# This script demonstrates how to create a new AppResults object, change its state
|
24
|
+
# and upload result files to it and download files from it.
|
25
|
+
|
26
|
+
opts = {
|
27
|
+
# FILL IN WITH YOUR APP VALUES HERE!
|
28
|
+
'client_id' => '<your client key>',
|
29
|
+
'client_secret' => '<your client secret>',
|
30
|
+
'access_token' => '<your access token>',
|
31
|
+
'app_session_id' => '<app session id>',
|
32
|
+
'basespace_url' => 'https://api.basespace.illumina.com/',
|
33
|
+
'api_version' => 'v1pre3',
|
34
|
+
}
|
35
|
+
|
36
|
+
# Test if client variables have been set.
|
37
|
+
unless opts.select{|k,v| v[/^<.*>$/]}.empty?
|
38
|
+
opts = Bio::BaseSpace.load_credentials
|
39
|
+
exit 1 unless opts
|
40
|
+
end
|
41
|
+
|
42
|
+
# First, create a client for making calls for this user session.
|
43
|
+
bs_api = BaseSpaceAPI.new(opts['client_id'], opts['client_secret'], opts['basespace_url'], opts['api_version'], opts['app_session_id'], opts['access_token'])
|
44
|
+
|
45
|
+
# Now we'll do some work of our own. First get a project to work on.
|
46
|
+
# We need write permission for the project we are working on,
|
47
|
+
# meaning we will need get a new token and instantiate a new BaseSpaceAPI.
|
48
|
+
prj = bs_api.get_project_by_id('89') # [TODO] Original ID '89' was not accessible. Writable project is needed.
|
49
|
+
|
50
|
+
# Assuming we have write access to the project
|
51
|
+
# we list the current App Results for the project.
|
52
|
+
statuses = ['Running']
|
53
|
+
app_res = prj.get_app_results(bs_api, {}, statuses) # [TODO] should introduce hash options / keyword arguments (in Ruby 2.0)
|
54
|
+
puts "The current running AppResults are #{app_res}"
|
55
|
+
puts
|
56
|
+
|
57
|
+
#
|
58
|
+
# Retrieving results and setting status
|
59
|
+
#
|
60
|
+
|
61
|
+
# To create an appResults for a project, simply give the name and description.
|
62
|
+
app_results = prj.create_app_result(bs_api, "testing", "this is my results")
|
63
|
+
puts "Some info about our new app results"
|
64
|
+
puts app_results
|
65
|
+
puts app_results.id
|
66
|
+
puts
|
67
|
+
puts "The app results also comes with a reference to our AppSession"
|
68
|
+
my_app_session = app_results.app_session
|
69
|
+
puts my_app_session
|
70
|
+
puts
|
71
|
+
|
72
|
+
# We can change the status of our AppSession and add a status-summary as follows.
|
73
|
+
my_app_session.set_status(bs_api, 'needsattention', "We worked hard, but encountered some trouble.")
|
74
|
+
puts "After a change of status of the app sessions we get #{my_app_session}"
|
75
|
+
puts
|
76
|
+
# We set our appSession back to running so we can do some more work.
|
77
|
+
my_app_session.set_status(bs_api, 'running', "Back on track")
|
78
|
+
|
79
|
+
|
80
|
+
# Let's list all AppResults again and see if our new object shows up.
|
81
|
+
app_res = prj.get_app_results(bs_api, {}, ['Running'])
|
82
|
+
puts "The updated app results are #{app_res}"
|
83
|
+
app_result2 = bs_api.get_app_result_by_id(app_results.id)
|
84
|
+
puts app_result2
|
85
|
+
puts
|
86
|
+
|
87
|
+
# Now we will make another AppResult and try to upload a file to it
|
88
|
+
app_results2 = prj.create_app_result(bs_api, "My second AppResult", "This one I will upload to")
|
89
|
+
app_results2.upload_file(bs_api, '/home/mkallberg/Desktop/testFile2.txt', 'BaseSpaceTestFile.txt', '/mydir/', 'text/plain')
|
90
|
+
puts "My AppResult number 2 #{app_results2}"
|
91
|
+
puts
|
92
|
+
|
93
|
+
# Let's see if our new file made it.
|
94
|
+
app_result_files = app_results2.get_files(bs_api)
|
95
|
+
puts "These are the files in the appResult"
|
96
|
+
puts app_result_files
|
97
|
+
f = app_result_files.last
|
98
|
+
|
99
|
+
# We can even download our newly uploaded file.
|
100
|
+
f = bs_api.get_file_by_id(f.id)
|
101
|
+
f.download_file(bs_api, '/home/mkallberg/Desktop/')
|
102
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2013 Toshiaki Katayama, Joachim Baran
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'basespace'
|
17
|
+
|
18
|
+
include Bio::BaseSpace
|
19
|
+
|
20
|
+
# This example demonstrates the billing methods of BaseSpace.
|
21
|
+
#
|
22
|
+
# Below a purchase is created, requiring the user to click 'Purchase' in a web
|
23
|
+
# browser. The purchase is then refunded, and the purchase is again retrieved
|
24
|
+
# via the API using the purchase id and tags, which are used by developers
|
25
|
+
# to help clarify exactly what was purchased.
|
26
|
+
#
|
27
|
+
# NOTE You will need to fill client values for your app below!
|
28
|
+
|
29
|
+
opts = {
|
30
|
+
# FILL IN WITH YOUR APP VALUES HERE!
|
31
|
+
'client_id' => '<your client key>', # from dev portal app Credentials tab
|
32
|
+
'client_secret' => '<your client secret>', # from dev portal app Credentials tab
|
33
|
+
'access_token' => '<your access token>', # from oauth2
|
34
|
+
'app_session_id' => '<app session id>', # from launching an app
|
35
|
+
'basespace_url' => 'https://api.basespace.illumina.com/',
|
36
|
+
'api_version' => 'v1pre3',
|
37
|
+
'basespace_store_url' => 'https://hoth-store.basespace.illumina.com/',
|
38
|
+
'product_id' => '', # from dev portal Pricing tab
|
39
|
+
}
|
40
|
+
|
41
|
+
# Test if client variables have been set.
|
42
|
+
unless opts.select{|k,v| v[/^<.*>$/]}.empty?
|
43
|
+
opts = Bio::BaseSpace.load_credentials
|
44
|
+
exit 1 unless opts
|
45
|
+
end
|
46
|
+
|
47
|
+
unless opts['client_id'] or opts['product_id']
|
48
|
+
raise "Please fill in client values (in the script) before running the script"
|
49
|
+
end
|
50
|
+
|
51
|
+
# First, create a client for making calls for this user session.
|
52
|
+
bill_api = BillingAPI.new(opts['basespace_store_url'], opts['api_version'], opts['app_session_id'], opts['access_token'])
|
53
|
+
|
54
|
+
# Create a non-consumable purchase.
|
55
|
+
#purch = bill_api.create_purchase([{'id':product_id,'quantity':4 }])
|
56
|
+
|
57
|
+
# Create a consumable purchase, and associated it with an AppSession
|
58
|
+
# also add tags to provide (fake) details about the purchase.
|
59
|
+
puts "Creating purchase"
|
60
|
+
# purch = billAPI.createPurchase({'id':product_id,'quantity':4, 'tags':["test","test_tag"] }, AppSessionId)
|
61
|
+
products = {
|
62
|
+
'id' => opts['product_id'],
|
63
|
+
'quantity' => 4,
|
64
|
+
'tags' => ["test", "test_tag"],
|
65
|
+
}
|
66
|
+
purch = bill_api.create_purchase(products, opts['app_session_id'])
|
67
|
+
|
68
|
+
# Record the purchase Id and RefundSecret for refunding later.
|
69
|
+
purchase_id = purch.id
|
70
|
+
refund_secret = purch.refund_secret
|
71
|
+
|
72
|
+
puts "Now complete the purchase in your web browser"
|
73
|
+
puts "CLOSE the browser window/tab after you click 'Purchase' (and don't proceed into the app)"
|
74
|
+
time.sleep(3)
|
75
|
+
## PAUSE HERE
|
76
|
+
link = purch.href_purchase_dialog
|
77
|
+
puts "Opening: #{link}"
|
78
|
+
host = RbConfig::CONFIG['host_os']
|
79
|
+
case host
|
80
|
+
when /mswin|mingw|cygwin/
|
81
|
+
system("start #{link}")
|
82
|
+
when /darwin/
|
83
|
+
system("open #{link}")
|
84
|
+
when /linux/
|
85
|
+
system("xdg-open #{link}")
|
86
|
+
end
|
87
|
+
puts "Waiting 30 seconds..."
|
88
|
+
time.sleep(30)
|
89
|
+
## PAUSE HERE
|
90
|
+
|
91
|
+
puts "Confirm the purchase"
|
92
|
+
post_purch = bill_api.get_purchase_by_id(purchase_id)
|
93
|
+
puts "The status of the purchase is now: " + post_purch.status
|
94
|
+
puts
|
95
|
+
|
96
|
+
puts "Refunding the Purchase"
|
97
|
+
puts
|
98
|
+
# NOTE We must use the same access token that was provided used for the purchase.
|
99
|
+
refunded_purchase = bill_api.refund_purchase(purchase_id, refund_secret, 'the product did not function well as a frisbee')
|
100
|
+
|
101
|
+
puts "Getting all purchases for the current user with the tags we used for the purchase above"
|
102
|
+
puts
|
103
|
+
#purch_prods = bill_api.get_user_products
|
104
|
+
purch_prods = bill_api.get_user_products('current', {'Tags' => 'test,test_tag'})
|
105
|
+
if purch_prods.nil? or purch_prods.empty?
|
106
|
+
puts "Hmmm, didn't find any purchases with these tags. Did everything go OK above?"
|
107
|
+
else
|
108
|
+
puts "For the first of these purchases:"
|
109
|
+
puts "Purchase Name: #{purch_prods[0].name}"
|
110
|
+
puts "Purchase Price: #{purch_prods[0].price}"
|
111
|
+
puts "Purchase Quantity: #{purch_prods[0].quantity}"
|
112
|
+
puts "Tags: #{purch_prods[0].tags}"
|
113
|
+
# Get the refund status of the purchase
|
114
|
+
puts "Getting the (refunded) Purchase we just made"
|
115
|
+
get_purch = bill_api.get_purchase_by_id(purch_prods[0].purchase_id)
|
116
|
+
puts "Refund Status: #{get_purch.refund_status}"
|
117
|
+
end
|
118
|
+
|
119
|
+
|
data/lib/basespace.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Copyright 2012-2013 Joachim Baran, Raoul Bonnal, Toshiaki Katayama, Francesco Strozzi
|
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
|
+
# Still need to check [TODO] sections
|
15
|
+
|
16
|
+
require 'basespace/api/api_client'
|
17
|
+
require 'basespace/api/base_api'
|
18
|
+
require 'basespace/api/billing_api'
|
19
|
+
require 'basespace/api/basespace_api'
|
20
|
+
require 'basespace/api/basespace_error'
|
21
|
+
|
22
|
+
require 'basespace/model'
|
23
|
+
require 'basespace/model/app_result'
|
24
|
+
require 'basespace/model/app_result_response'
|
25
|
+
require 'basespace/model/app_session'
|
26
|
+
require 'basespace/model/app_session_compact'
|
27
|
+
require 'basespace/model/app_session_launch_object'
|
28
|
+
require 'basespace/model/app_session_response'
|
29
|
+
require 'basespace/model/application'
|
30
|
+
require 'basespace/model/application_compact'
|
31
|
+
require 'basespace/model/basespace_model'
|
32
|
+
require 'basespace/model/coverage'
|
33
|
+
require 'basespace/model/coverage_meta_response'
|
34
|
+
require 'basespace/model/coverage_metadata'
|
35
|
+
require 'basespace/model/coverage_response'
|
36
|
+
require 'basespace/model/file'
|
37
|
+
require 'basespace/model/file_response'
|
38
|
+
require 'basespace/model/genome_response'
|
39
|
+
require 'basespace/model/genome_v1'
|
40
|
+
require 'basespace/model/list_response'
|
41
|
+
#require 'basespace/model/multipart_upload'
|
42
|
+
require 'basespace/model/product'
|
43
|
+
require 'basespace/model/project'
|
44
|
+
require 'basespace/model/project_response'
|
45
|
+
require 'basespace/model/purchase'
|
46
|
+
require 'basespace/model/purchase_response'
|
47
|
+
require 'basespace/model/purchased_product'
|
48
|
+
require 'basespace/model/query_parameters'
|
49
|
+
require 'basespace/model/query_parameters_purchased_product'
|
50
|
+
require 'basespace/model/refund_purchase_response'
|
51
|
+
require 'basespace/model/resource_list'
|
52
|
+
require 'basespace/model/response_status'
|
53
|
+
require 'basespace/model/run_compact'
|
54
|
+
require 'basespace/model/sample'
|
55
|
+
require 'basespace/model/sample_response'
|
56
|
+
require 'basespace/model/user'
|
57
|
+
require 'basespace/model/user_compact'
|
58
|
+
require 'basespace/model/user_response'
|
59
|
+
require 'basespace/model/variant'
|
60
|
+
require 'basespace/model/variant_header'
|
61
|
+
require 'basespace/model/variant_info'
|
62
|
+
require 'basespace/model/variants_header_response'
|
63
|
+
|
64
|
+
require 'json'
|
65
|
+
|
66
|
+
module Bio
|
67
|
+
|
68
|
+
# BaseSpace Ruby SDK is to be used in the development of Apps and scripts
|
69
|
+
# for working with Illumina's BaseSpace cloud-computing solution for next-gen
|
70
|
+
# sequencing data analysis.
|
71
|
+
module BaseSpace
|
72
|
+
|
73
|
+
# Loads login and authentication credentials from a JSON file.
|
74
|
+
#
|
75
|
+
# If the environment variable "BASESPACE_CREDENTIALS" is set, then the
|
76
|
+
# path to the JSON files is taken from there. Otherwise, the current
|
77
|
+
# directory is searched for the file "credentials.json".
|
78
|
+
#
|
79
|
+
# On success, returns a hash with the values for
|
80
|
+
# * client_id
|
81
|
+
# * client_secret
|
82
|
+
# * access_token
|
83
|
+
# * app_session_id
|
84
|
+
# * basespace_url
|
85
|
+
# * api_version
|
86
|
+
#
|
87
|
+
# On failure, returns nil.
|
88
|
+
def self.load_credentials
|
89
|
+
filename = "credentials.json"
|
90
|
+
if ENV['BASESPACE_CREDENTIALS']
|
91
|
+
jsonfile = ENV['BASESPACE_CREDENTIALS']
|
92
|
+
else
|
93
|
+
jsonfile = ::File.join('.', filename)
|
94
|
+
end
|
95
|
+
if ::File.exists?(jsonfile)
|
96
|
+
hash = JSON.parse(::File.read(jsonfile))
|
97
|
+
if $DEBUG
|
98
|
+
$stderr.puts " # ----- Bio::BaseSpace.load_credientials ----- "
|
99
|
+
$stderr.puts " # Loaded credentials from #{jsonfile}"
|
100
|
+
$stderr.puts " # "
|
101
|
+
end
|
102
|
+
else
|
103
|
+
hash = nil
|
104
|
+
$stderr.puts " # ----- Bio::BaseSpace.load_credientials ----- "
|
105
|
+
$stderr.puts " # You can put your credentials for the BaseSpace in the"
|
106
|
+
$stderr.puts " # #{jsonfile}"
|
107
|
+
$stderr.puts " # file or point to the file with an environmental variable"
|
108
|
+
$stderr.puts " # export BASESPACE_CREDENTIALS=/path/to/your/#{filename}"
|
109
|
+
$stderr.puts " # in the following format:"
|
110
|
+
data = {
|
111
|
+
'client_id' => '<your client id>',
|
112
|
+
'client_secret' => '<your client secret>',
|
113
|
+
'access_token' => '<your access token>',
|
114
|
+
'app_session_id' => '<app session id>',
|
115
|
+
'basespace_url' => 'https://api.basespace.illumina.com/',
|
116
|
+
'api_version' => 'v1pre3',
|
117
|
+
}
|
118
|
+
$stderr.puts JSON.pretty_generate(JSON.parse(data.to_json))
|
119
|
+
end
|
120
|
+
return hash
|
121
|
+
end
|
122
|
+
|
123
|
+
end # BaseSpace
|
124
|
+
end # Bio
|
125
|
+
|
126
|
+
|
@@ -0,0 +1,313 @@
|
|
1
|
+
# Copyright 2012-2013 Joachim Baran, Toshiaki Katayama
|
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/basespace_error'
|
15
|
+
|
16
|
+
require 'net/https'
|
17
|
+
require 'uri'
|
18
|
+
require 'json'
|
19
|
+
require 'date'
|
20
|
+
|
21
|
+
Net::HTTP.version_1_2
|
22
|
+
|
23
|
+
module Bio
|
24
|
+
module BaseSpace
|
25
|
+
|
26
|
+
# This class provides wrapper methods to the BaseSpace API RESTful interface. It also
|
27
|
+
# handles serialization and deserialization of objects (Ruby to/from JSON). It is primarily
|
28
|
+
# used as a helper class for BaseSpaceAPI.
|
29
|
+
class APIClient
|
30
|
+
attr_accessor :api_key, :api_server, :timeout
|
31
|
+
|
32
|
+
# Create a new instance that will carry out REST calls.
|
33
|
+
#
|
34
|
+
# +access_token+:: Access token that is provided by App triggering.
|
35
|
+
# +api_server+:: URI of the BaseSpace API server.
|
36
|
+
# +timeout+:: Timeout for REST calls.
|
37
|
+
def initialize(access_token = nil, api_server = nil, timeout = 10)
|
38
|
+
if $DEBUG
|
39
|
+
$stderr.puts " # ----- BaseAPI#initialize ----- "
|
40
|
+
$stderr.puts " # caller: #{caller}"
|
41
|
+
$stderr.puts " # access_token: #{access_token}"
|
42
|
+
$stderr.puts " # api_server: #{api_server}"
|
43
|
+
$stderr.puts " # timeout: #{timeout}"
|
44
|
+
$stderr.puts " # "
|
45
|
+
end
|
46
|
+
raise UndefinedParameterError.new('AccessToken') unless access_token
|
47
|
+
@api_key = access_token
|
48
|
+
@api_server = api_server
|
49
|
+
@timeout = timeout
|
50
|
+
end
|
51
|
+
|
52
|
+
# POST data to a provided URI.
|
53
|
+
#
|
54
|
+
# +resource_path+:: URI to which the data should be POSTed.
|
55
|
+
# +post_data+:: Hash that contains the data.
|
56
|
+
# +headers+:: Header of the POST call.
|
57
|
+
# +data+:: (unused; TODO)
|
58
|
+
def force_post_call(resource_path, post_data, headers, data = nil)
|
59
|
+
# [TODO] Confirm whether we can expect those two parameters are Hash objects:
|
60
|
+
# headers = { "key" => "value" }
|
61
|
+
# post_data = { "key" => "value" }
|
62
|
+
uri = URI.parse(resource_path)
|
63
|
+
# If headers are not needed, the following line should be enough:
|
64
|
+
# return Net::HTTP.post_form(uri, post_data).body
|
65
|
+
http_opts = {}
|
66
|
+
if uri.scheme == "https"
|
67
|
+
http_opts[:use_ssl] = true
|
68
|
+
end
|
69
|
+
res = Net::HTTP.start(uri.host, uri.port, http_opts) { |http|
|
70
|
+
encoded_data = hash2urlencode(post_data)
|
71
|
+
http.post(uri.path, encoded_data, headers)
|
72
|
+
}
|
73
|
+
return res.body
|
74
|
+
end
|
75
|
+
|
76
|
+
# URL encode a Hash of data values.
|
77
|
+
#
|
78
|
+
# +hash+:: data encoded in a Hash.
|
79
|
+
def hash2urlencode(hash)
|
80
|
+
# URI.escape (alias URI.encode) is obsolete since Ruby 1.9.2.
|
81
|
+
#return hash.map{|k,v| URI.encode(k.to_s) + "=" + URI.encode(v.to_s)}.join("&")
|
82
|
+
return hash.map{|k,v| URI.encode_www_form_component(k.to_s) + "=" + URI.encode_www_form_component(v.to_s)}.join("&")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Makes a PUT call to a given URI for depositing file contents.
|
86
|
+
#
|
87
|
+
# +resource_path+:: URI to which the data should be transferred.
|
88
|
+
# +post_data+:: (unused; TODO)
|
89
|
+
# +headers+:: Header of the PUT call.
|
90
|
+
# +trans_file+:: Path to the file that should be transferred.
|
91
|
+
def put_call(resource_path, post_data, headers, trans_file)
|
92
|
+
return %x(curl -H "x-access-token:#{@api_key}" -H "Content-MD5:#{headers['Content-MD5'].strip}" -T "#{trans_file}" -X PUT #{resource_path})
|
93
|
+
end
|
94
|
+
|
95
|
+
# Deserialize a boolean value to a Ruby object.
|
96
|
+
#
|
97
|
+
# +value+:: serialized representation of the boolean value.
|
98
|
+
def bool(value)
|
99
|
+
case value
|
100
|
+
when nil, false, 0, 'nil', 'false', '0', 'None'
|
101
|
+
result = false
|
102
|
+
else
|
103
|
+
result = true
|
104
|
+
end
|
105
|
+
return result
|
106
|
+
end
|
107
|
+
|
108
|
+
# Carries out a RESTful operation on the BaseSpace API.
|
109
|
+
#
|
110
|
+
# TODO Need check. Logic in this method is rather complicated...
|
111
|
+
#
|
112
|
+
# +resource_path+:: URI that should be used for the API call.
|
113
|
+
# +method+:: HTTP method for the rest call (GET, POST, DELETE, etc.)
|
114
|
+
# +query_params+:: query parameters that should be sent along to the API.
|
115
|
+
# +post_data+:: Hash that contains data to be transferred.
|
116
|
+
# +header_params+:: Additional settings that should be transferred in the HTTP header.
|
117
|
+
# +force_post+:: Truth value that indicates whether a POST should be forced.
|
118
|
+
def call_api(resource_path, method, query_params, post_data, header_params = nil, force_post = false)
|
119
|
+
url = @api_server + resource_path
|
120
|
+
|
121
|
+
headers = header_params.dup
|
122
|
+
headers['Content-Type'] = 'application/json' if not headers.has_key?('Content-Type') and not method == 'PUT'
|
123
|
+
# include access token in header
|
124
|
+
headers['Authorization'] = "Bearer #{@api_key}"
|
125
|
+
|
126
|
+
uri = request = response = data = cgi_params = nil
|
127
|
+
|
128
|
+
if query_params
|
129
|
+
# Need to remove None (in Python, nil in Ruby) values, these should not be sent
|
130
|
+
sent_query_params = {}
|
131
|
+
query_params.each do |param, value|
|
132
|
+
sent_query_params[param] = value if bool(value)
|
133
|
+
end
|
134
|
+
cgi_params = hash2urlencode(sent_query_params)
|
135
|
+
end
|
136
|
+
|
137
|
+
if $DEBUG
|
138
|
+
$stderr.puts " # ----- APIClient#call_api ----- "
|
139
|
+
$stderr.puts " # caller: #{caller}"
|
140
|
+
$stderr.puts " # url: #{url}"
|
141
|
+
$stderr.puts " # method: #{method}"
|
142
|
+
$stderr.puts " # headers: #{headers}"
|
143
|
+
$stderr.puts " # cgi_params: #{cgi_params}"
|
144
|
+
$stderr.puts " # post_data: #{post_data}"
|
145
|
+
$stderr.puts " # "
|
146
|
+
end
|
147
|
+
|
148
|
+
case method
|
149
|
+
when 'GET'
|
150
|
+
if cgi_params
|
151
|
+
url += "?#{cgi_params}"
|
152
|
+
end
|
153
|
+
# [TODO] confirm this works or not
|
154
|
+
#request = urllib2.Request(url, headers)
|
155
|
+
uri = URI.parse(url)
|
156
|
+
request = Net::HTTP::Get.new(uri.path, headers)
|
157
|
+
when 'POST', 'PUT', 'DELETE'
|
158
|
+
if cgi_params
|
159
|
+
force_post_url = url
|
160
|
+
url += "?#{cgi_params}"
|
161
|
+
end
|
162
|
+
if post_data
|
163
|
+
# [TODO] Do we need to skip String, Integer, Float and bool in Ruby?
|
164
|
+
data = post_data.to_json # if not [str, int, float, bool].include?(type(post_data))
|
165
|
+
end
|
166
|
+
if force_post
|
167
|
+
response = force_post_call(force_post_url, sent_query_params, headers)
|
168
|
+
else
|
169
|
+
data = '\n' if data and data.empty? # temp fix, in case is no data in the file, to prevent post request from failing
|
170
|
+
# [TODO] confirm this works or not
|
171
|
+
#request = urllib2.Request(url, headers, data)#, @timeout)
|
172
|
+
uri = URI.parse(url)
|
173
|
+
request = Net::HTTP::Post.new(uri.path, headers)
|
174
|
+
end
|
175
|
+
if ['PUT', 'DELETE'].include?(method) # urllib doesnt do put and delete, default to pycurl here
|
176
|
+
response = put_call(url, query_params, headers, data)
|
177
|
+
response = response.split.last
|
178
|
+
end
|
179
|
+
else
|
180
|
+
raise "Method #{method} is not recognized."
|
181
|
+
end
|
182
|
+
|
183
|
+
# Make the request, request may raise 403 forbidden, or 404 non-response
|
184
|
+
if not force_post and not ['PUT', 'DELETE'].include?(method) # the normal case
|
185
|
+
# puts url
|
186
|
+
# puts request
|
187
|
+
# puts "request with timeout=#{@timeout}"
|
188
|
+
# [TODO] confirm this works or not
|
189
|
+
#response = urllib2.urlopen(request, @timeout).read()
|
190
|
+
http_opts = {}
|
191
|
+
if uri.scheme == "https"
|
192
|
+
http_opts[:use_ssl] = true
|
193
|
+
end
|
194
|
+
response = Net::HTTP.start(uri.host, uri.port, http_opts) { |http|
|
195
|
+
http.request(request)
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
begin
|
200
|
+
data = JSON.parse(response.body)
|
201
|
+
rescue => err
|
202
|
+
$stderr.puts " # ----- APIClient#call_api ----- "
|
203
|
+
$stderr.puts " # Error: #{err}"
|
204
|
+
$stderr.puts " # "
|
205
|
+
data = nil
|
206
|
+
end
|
207
|
+
return data
|
208
|
+
end
|
209
|
+
|
210
|
+
# Serialize a list to a CSV string, if necessary.
|
211
|
+
#
|
212
|
+
# +obj+:: Data object to be serialized.
|
213
|
+
def to_path_value(obj)
|
214
|
+
if obj.kind_of?(Array)
|
215
|
+
return obj.join(',')
|
216
|
+
else
|
217
|
+
return obj
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# Deserialize a JSON string into an object.
|
222
|
+
#
|
223
|
+
# +obj+:: String or object to be deserialized.
|
224
|
+
# +obj_class+:: Class literal for deserialzied object, or string of class name.
|
225
|
+
def deserialize(obj, obj_class)
|
226
|
+
case obj_class.downcase
|
227
|
+
when 'str'
|
228
|
+
return obj.to_s
|
229
|
+
when 'int'
|
230
|
+
return obj.to_i
|
231
|
+
when 'float'
|
232
|
+
return obj.to_f
|
233
|
+
when 'bool'
|
234
|
+
return bool(obj)
|
235
|
+
when 'file'
|
236
|
+
# Bio::BaseSpace::File
|
237
|
+
instance = File.new
|
238
|
+
else
|
239
|
+
# models in BaseSpace
|
240
|
+
klass = Object.const_get(obj_class)
|
241
|
+
instance = klass.new
|
242
|
+
end
|
243
|
+
|
244
|
+
if $DEBUG
|
245
|
+
$stderr.puts " # ----- APIClient#deserialize ----- "
|
246
|
+
$stderr.puts " # caller: #{caller}"
|
247
|
+
$stderr.puts " # obj_class: #{obj_class}"
|
248
|
+
$stderr.puts " # obj: #{obj}" # JSON.pretty_generate(obj)
|
249
|
+
$stderr.puts " # "
|
250
|
+
end
|
251
|
+
|
252
|
+
instance.swagger_types.each do |attr, attr_type|
|
253
|
+
if obj.has_key?(attr) or obj.has_key?(attr.to_s)
|
254
|
+
if $DEBUG
|
255
|
+
$stderr.puts " # # ----- APIClient#deserialize/swagger_types ----- "
|
256
|
+
$stderr.puts " # # attr: #{attr}"
|
257
|
+
$stderr.puts " # # attr_type: #{attr_type}"
|
258
|
+
$stderr.puts " # # value: #{obj[attr]}"
|
259
|
+
$stderr.puts " # # "
|
260
|
+
end
|
261
|
+
value = obj[attr]
|
262
|
+
case attr_type.downcase
|
263
|
+
when 'str'
|
264
|
+
instance.set_attr(attr, value.to_s)
|
265
|
+
when 'int'
|
266
|
+
instance.set_attr(attr, value.to_i)
|
267
|
+
when 'float'
|
268
|
+
instance.set_attr(attr, value.to_f)
|
269
|
+
when 'datetime'
|
270
|
+
instance.set_attr(attr, DateTime.parse(value))
|
271
|
+
when 'bool'
|
272
|
+
instance.set_attr(attr, bool(value))
|
273
|
+
when /list</
|
274
|
+
sub_class = attr_type[/list<(.*)>/, 1]
|
275
|
+
sub_values = []
|
276
|
+
value.each do |sub_value|
|
277
|
+
sub_values << deserialize(sub_value, sub_class)
|
278
|
+
end
|
279
|
+
instance.set_attr(attr, sub_values)
|
280
|
+
when 'dict' # support for parsing dictionary
|
281
|
+
if $DEBUG
|
282
|
+
$stderr.puts " # # # ----- APIClient#deserialize/swagger_types/dict ----- "
|
283
|
+
$stderr.puts " # # # dict: #{value}"
|
284
|
+
$stderr.puts " # # # "
|
285
|
+
end
|
286
|
+
# [TODO] May need to convert value -> Hash (check in what format the value is passed)
|
287
|
+
instance.set_attr(attr, value)
|
288
|
+
else
|
289
|
+
if $DEBUG
|
290
|
+
# print "recursive call w/ " + attrType
|
291
|
+
$stderr.puts " # # # ----- APIClient#deserialize/swagger_types/recursive call ----- "
|
292
|
+
$stderr.puts " # # # attr: #{attr}"
|
293
|
+
$stderr.puts " # # # attr_type: #{attr_type}"
|
294
|
+
$stderr.puts " # # # value: #{value}"
|
295
|
+
$stderr.puts " # # # "
|
296
|
+
end
|
297
|
+
instance.set_attr(attr, deserialize(value, attr_type))
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
if $DEBUG
|
302
|
+
$stderr.puts " # # ----- APIClient#deserialize/instance ----- "
|
303
|
+
$stderr.puts " # # instance: #{instance.attributes.inspect}"
|
304
|
+
$stderr.puts " # # "
|
305
|
+
end
|
306
|
+
return instance
|
307
|
+
end
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
end # module BaseSpace
|
312
|
+
end # module Bio
|
313
|
+
|