right_api_client 1.5.9 → 1.5.12

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.
@@ -0,0 +1,30 @@
1
+
2
+ module RightApi
3
+
4
+ class ApiError < RuntimeError
5
+
6
+ def initialize(request, response)
7
+
8
+ @request, @response = request, response
9
+
10
+ super(
11
+ prefix +
12
+ "HTTP Code: #{response.code.to_s}, " +
13
+ "Response body: #{response.body}")
14
+ end
15
+
16
+ def prefix
17
+
18
+ 'Error: '
19
+ end
20
+ end
21
+
22
+ class UnknownRouteError < ApiError
23
+
24
+ def prefix
25
+
26
+ 'Unknown action or route. '
27
+ end
28
+ end
29
+ end
30
+
@@ -58,7 +58,9 @@ module RightApi::Helper
58
58
  # get the resource_type
59
59
  # Special case: calling .data you don't want a resources object back
60
60
  # but rather all its details since you cannot do a show
61
- return RightApi::ResourceDetail.new(client, *client.do_get(hrefs.first, *args)) if rel == :data
61
+ return RightApi::ResourceDetail.new(
62
+ client, *client.send(:do_get, hrefs.first, *args)
63
+ ) if rel == :data
62
64
 
63
65
  if is_singular?(rel)
64
66
  # Then the href will be: /resource_type/:id
@@ -150,8 +152,19 @@ module RightApi::Helper
150
152
 
151
153
  # Helper method that checks whether the string is singular
152
154
  def is_singular?(str)
153
- return true if ['data'].include?(str.to_s)
154
- (str.to_s)[-1, 1] != 's' # use legacy syntax for Ruby 1.8.7
155
+ test_str = str.to_s
156
+ return true if ['data'].include?(test_str)
157
+
158
+ case test_str
159
+ when "audit_entry"
160
+ return true
161
+ when "ip_address"
162
+ return true
163
+ when "process"
164
+ return true
165
+ else
166
+ (test_str)[-1, 1] != 's' # use legacy syntax for Ruby 1.8.7
167
+ end
155
168
  end
156
169
 
157
170
  # Does not modify links
@@ -172,10 +185,58 @@ module RightApi::Helper
172
185
  return nil
173
186
  end
174
187
 
188
+ # HACK: instead of pulling in activesupport gem, just hardcode some words
189
+ def simple_singularize(word)
190
+ case word
191
+ when "audit_entries"
192
+ "audit_entry"
193
+ when "ip_addresses"
194
+ "ip_address"
195
+ when "processes"
196
+ "process"
197
+ else
198
+ word.chop
199
+ end
200
+ end
201
+
175
202
  # Will not change obj
176
203
  def get_singular(obj)
177
- str = obj.to_s
178
- return 'audit_entry' if str == 'audit_entries'
179
- str.chomp('s')
204
+ simple_singularize(obj.to_s.downcase)
180
205
  end
206
+
207
+ # rest client does not post params correctly for all the keys whose values are arrays of hashes
208
+ # @see http://stackoverflow.com/questions/6436110/restclient-strips-out-the-array-of-hashes-parameter-with-just-the-last-hash
209
+ #
210
+ def fix_array_of_hashes(args, top_level = true)
211
+
212
+ if args.is_a?(Array)
213
+
214
+ #recursively fix each element of the array
215
+ #
216
+ args.collect{|a| fix_array_of_hashes(a, false)}
217
+ elsif args.is_a?(Hash)
218
+
219
+ args.inject({}) do |res, (k, v)|
220
+ key = k
221
+ if v.is_a?(Array) && !v.empty? && v.first.is_a?(Hash)
222
+ if top_level
223
+ # signal to Rails that this really is an array
224
+ key = k.to_s + '[]'
225
+ value = fix_array_of_hashes(v, false)
226
+ else
227
+ # signal to Rails that this really is an array
228
+ value = {'' => fix_array_of_hashes(v, false)}
229
+ end
230
+ else
231
+ value = fix_array_of_hashes(v, false)
232
+ end
233
+ res[key] = value
234
+ res
235
+ end
236
+ else
237
+
238
+ args
239
+ end
240
+ end
241
+
181
242
  end
@@ -40,26 +40,25 @@ module RightApi
40
40
  @href = href
41
41
 
42
42
  # Add destroy method to relevant resources
43
- define_instance_method('destroy') do
44
- client.do_delete(href)
43
+ define_instance_method('destroy') do |*args|
44
+ client.send(:do_delete, href, *args)
45
45
  end
46
46
 
47
47
  # Add update method to relevant resources
48
48
  define_instance_method('update') do |*args|
49
- client.do_put(href, *args)
49
+ client.send(:do_put, href, *args)
50
50
  end
51
51
 
52
52
  # Add show method to relevant resources
53
53
  define_instance_method('show') do |*args|
54
- RightApi::ResourceDetail.new(client, *client.do_get(href, *args))
54
+ RightApi::ResourceDetail.new(client, *client.send(:do_get, href, *args))
55
55
  end
56
56
  end
57
57
 
58
- #Any other method other than standard actions(show,update,destroy) is simply appended to the href and
59
- #called with a POST.
58
+ # Any other method other than standard actions(show,update,destroy)
59
+ # is simply appended to the href and called with a POST.
60
60
  def method_missing(m, *args)
61
- action_href = href + "/" + m.to_s
62
- client.do_post(action_href, *args)
61
+ client.send(:do_post, [ href, m.to_s ].join('/'), *args)
63
62
  end
64
63
  end
65
64
  end
@@ -41,8 +41,7 @@ module RightApi
41
41
  actions << action_name.to_sym
42
42
 
43
43
  define_instance_method(action_name.to_sym) do |*args|
44
- action_href = hash['href'] + "/" + action['rel']
45
- client.do_post(action_href, *args)
44
+ client.send(:do_post, "#{hash['href']}/#{action['rel']}", *args)
46
45
  end
47
46
  end
48
47
 
@@ -91,13 +90,19 @@ module RightApi
91
90
  end
92
91
 
93
92
  # Add destroy method to relevant resources
94
- define_instance_method('destroy') do
95
- client.do_delete(href)
93
+ define_instance_method('destroy') do |*args|
94
+ client.send(:do_delete, href, *args)
96
95
  end
97
96
 
98
97
  # Add update method to relevant resources
99
98
  define_instance_method('update') do |*args|
100
- client.do_put(href, *args)
99
+
100
+ if resource_type == 'account' # HACK: handle child_account update specially
101
+ update_href = href.sub(/account/, 'child_account')
102
+ client.send(:do_put, update_href, *args)
103
+ else
104
+ client.send(:do_put, href, *args)
105
+ end
101
106
  end
102
107
 
103
108
  # Add show method to relevant resources
@@ -106,12 +111,11 @@ module RightApi
106
111
  end
107
112
  end
108
113
 
109
- #Any other method other than standard actions(show,update,destroy) is simply appended to the href and
110
- #called with a POST.
114
+ # Any other method other than standard actions(show,update,destroy)
115
+ # is simply appended to the href and called with a POST.
111
116
  def method_missing(m, *args)
112
- #The method href would have been defined while initializing the object
113
- action_href = href + "/" + m.to_s
114
- client.do_post(action_href, *args)
117
+ # the method href would have been defined while initializing the object
118
+ client.send(:do_post, [ href, m.to_s ].join('/'), *args)
115
119
  end
116
120
  end
117
121
  end
@@ -25,16 +25,16 @@ module RightApi
25
25
  @resource_type = resource_type
26
26
  # Add create methods for the relevant root RightApi::Resources
27
27
  self.define_instance_method('create') do |*args|
28
- client.do_post(path, *args)
28
+ client.send(:do_post, path, *args)
29
29
  end
30
30
 
31
31
  # Add in index methods for the relevant root RightApi::Resources
32
32
  self.define_instance_method('index') do |*args|
33
33
  # Session uses .index like a .show (so need to treat it as a special case)
34
34
  if resource_type == 'session'
35
- ResourceDetail.new(client, *client.do_get(path, *args))
35
+ ResourceDetail.new(client, *client.send(:do_get, path, *args))
36
36
  else
37
- RightApi::Resource.process(client, *client.do_get(path, *args))
37
+ RightApi::Resource.process(client, *client.send(:do_get, path, *args))
38
38
  end
39
39
  end
40
40
 
@@ -43,16 +43,15 @@ module RightApi
43
43
  # Insert_in_path will NOT modify path
44
44
  action_path = insert_in_path(path, meth)
45
45
  self.define_instance_method(meth) do |*args|
46
- client.send action, action_path, *args
46
+ client.send(action, action_path, *args)
47
47
  end
48
48
  end if Helper::RESOURCE_SPECIAL_ACTIONS[resource_type]
49
49
  end
50
50
 
51
- #Any other method other than standard actions(create, index) is simply appended to the path and
52
- #called with a POST.
51
+ # Any other method other than standard actions (create, index)
52
+ # is simply appended to the href and called with a POST.
53
53
  def method_missing(m, *args)
54
- action_path = path + "/" + m.to_s
55
- client.do_post(action_path, *args)
54
+ client.send(:do_post, [ href, m.to_s ].join('/'), *args)
56
55
  end
57
56
  end
58
57
  end
@@ -2,7 +2,7 @@
2
2
  module RightApi
3
3
  class Client
4
4
  API_VERSION = '1.5'
5
- CLIENT_VERSION = '9'
5
+ CLIENT_VERSION = '12'
6
6
  VERSION = "#{API_VERSION}.#{CLIENT_VERSION}"
7
7
  end
8
8
  end
@@ -8,8 +8,7 @@ require 'irb'
8
8
 
9
9
  begin
10
10
  @client = RightApi::Client.new(YAML.load_file(File.expand_path('../config/login.yml', __FILE__)))
11
- puts "logged-in to the API, use the '@client' variable to use the client, e.g. '@client.session.index.message' will output:"
12
- puts @client.session.index.message
11
+ puts "logged-in to the API, use the '@client' variable to use the client"
13
12
  end
14
13
 
15
14
  IRB.start
@@ -1,29 +1,29 @@
1
1
  require File.expand_path('../lib/right_api_client/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.name = 'right_api_client'
5
- s.version = RightApi::Client::VERSION
6
- s.platform = Gem::Platform::RUBY
7
- s.date = Time.now.utc.strftime("%Y-%m-%d")
8
- s.require_path = 'lib'
9
- s.authors = [ 'RightScale, Inc.' ]
10
- s.email = [ 'rubygems@rightscale.com' ]
11
- s.homepage = 'https://github.com/rightscale/right_api_client'
12
- s.summary = 'RightScale MultiCloud API HTTP Client'
13
- s.description = %{
14
- The right_api_client gem simplifies the use of RightScale's MultiCloud API. It provides
15
- a simple object model of the API resources, and handles all of the fine details involved
16
- in making HTTP calls and translating their responses.
4
+ s.name = 'right_api_client'
5
+ s.version = RightApi::Client::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.date = Time.now.utc.strftime("%Y-%m-%d")
8
+ s.require_path = 'lib'
9
+ s.authors = [ 'RightScale, Inc.' ]
10
+ s.email = [ 'rubygems@rightscale.com' ]
11
+ s.homepage = 'https://github.com/rightscale/right_api_client'
12
+ s.summary = 'RightScale MultiCloud API HTTP Client'
13
+ s.description = %{
14
+ The right_api_client gem simplifies the use of RightScale's MultiCloud API.
15
+ It provides a simple object model of the API resources, and handles all of the
16
+ fine details involved in making HTTP calls and translating their responses.
17
17
  }
18
- s.files = `git ls-files`.split(' ')
19
- s.test_files = `git ls-files spec config`.split(' ')
18
+ s.files = `git ls-files`.split(' ')
19
+ s.test_files = `git ls-files spec config`.split(' ')
20
20
  s.rubygems_version = '1.8.17'
21
21
 
22
22
  s.add_runtime_dependency 'json'
23
- s.add_runtime_dependency 'rest-client', '1.6.7'
23
+ s.add_runtime_dependency 'rest-client', '~> 1.6'
24
24
 
25
25
  s.add_development_dependency 'rake', '0.8.7'
26
- s.add_development_dependency 'rspec', '1.3.0'
26
+ s.add_development_dependency 'rspec', '2.9.0'
27
27
  s.add_development_dependency 'flexmock', '0.8.7'
28
28
  s.add_development_dependency 'simplecov', '0.4.2'
29
29
  s.add_development_dependency 'bundler'
@@ -1,5 +1,5 @@
1
1
  ruby do
2
- version 'ree-1.8.7-2011.02'
2
+ version 'ree-1.8.7-2012.02'
3
3
  rubygems '1.8.17'
4
4
  gemset 'right_api_client'
5
5
  end
@@ -1,12 +1,10 @@
1
-
2
- require 'spec_helper'
3
-
1
+ require File.expand_path('../../spec_helper', __FILE__)
4
2
 
5
3
  describe RightApi::Client do
6
4
 
7
5
  before(:all) do
8
6
 
9
- creds = File.expand_path('../../config/login.yml', __FILE__)
7
+ creds = File.expand_path('../../../config/login.yml', __FILE__)
10
8
 
11
9
  begin
12
10
  @client = RightApi::Client.new(YAML.load_file(creds))
@@ -1,31 +1,33 @@
1
- require File.expand_path('../spec_helper', __FILE__)
2
- require 'yaml'
1
+ require File.expand_path('../../spec_helper', __FILE__)
3
2
 
4
3
  describe RightApi::Client do
5
- context "Given a valid set of credentials in the config/login.yml file" do
4
+
5
+ context "given a valid set of credentials in the config/login.yml file" do
6
+
6
7
  before(:all) do
7
- @creds = '../../config/login.yml'
8
+ @creds = '../../../config/login.yml'
8
9
  begin
9
10
  @client = RightApi::Client.new(YAML.load_file(File.expand_path(@creds, __FILE__)))
10
- rescue Exception => e
11
+ rescue => e
11
12
  puts "WARNING: The following specs need a valid set of credentials as they are integration tests that can only be done by calling the API server"
12
13
  puts e.message
14
+ puts e.backtrace
13
15
  end
14
16
  end
15
17
 
16
- it "Should login" do
17
- @client.headers[:cookies].should_not be_nil
18
+ it "logs in" do
19
+ @client.send(:headers)[:cookies].should_not be_nil
18
20
  @client.session.index.message.should == 'You have successfully logged into the RightScale API.'
19
21
  end
20
22
 
21
- it "Should return valid cookies" do
23
+ it "returns valid cookies" do
22
24
  @client.cookies.class.should == Hash
23
25
  @client.cookies['_session_id'].should_not be_nil
24
26
  @client.cookies['domain'].should match /rightscale.com$/
25
27
  @client.cookies.keys.sort.last.should match /^rs_gbl/ # HACK: not quite sane sanity check
26
28
  end
27
29
 
28
- it "Should accept a cookie argument when creating a new client" do
30
+ it "accepts a cookie argument when creating a new client" do
29
31
  my_hash = YAML.load_file(File.expand_path(@creds, __FILE__))
30
32
  my_hash.delete(:email)
31
33
  my_hash.delete(:password)
@@ -35,12 +37,27 @@ describe RightApi::Client do
35
37
  client1.cookies.should == @client.cookies
36
38
  end
37
39
 
38
- it "Should accept a YAML argument when creating a new client" do
40
+ it "timestamps cookies" do
41
+
42
+ @client.cookies.timestamp.should_not == nil
43
+ end
44
+
45
+ it "keeps track of the cookies all the time" do
46
+
47
+ t0 = @client.cookies.timestamp
48
+
49
+ @client.deployments.index
50
+ t1 = @client.cookies.timestamp
51
+
52
+ t0.to_f.should < t1.to_f
53
+ end
54
+
55
+ it "accepts a YAML argument when creating a new client" do
39
56
  client2 = RightApi::Client.new(YAML.load_file(File.expand_path(@creds, __FILE__)))
40
57
  client2.cookies.should_not == @client.cookies
41
58
  end
42
59
 
43
- it "Should send post/get/put/delete requests to the server correctly" do
60
+ it "sends post/get/put/delete requests to the server correctly" do
44
61
  new_deployment = @client.deployments.create(:deployment => {:name => 'test'})
45
62
  new_deployment2 = @client.deployments.create(:deployment => {:name => 'test2'})
46
63
  new_deployment.class.should == RightApi::Resource
@@ -55,7 +72,7 @@ describe RightApi::Client do
55
72
  deployment.show.name.should == 'test2'
56
73
 
57
74
  # Tags are a bit special as they use POST and return content type so they need specific tests
58
- @client.tags.multi_add("resource_hrefs[]=#{deployment.show.href}&resource_hrefs[]=#{new_deployment2.show.href}&tags[]=tag1").should == ""
75
+ @client.tags.multi_add("resource_hrefs[]=#{deployment.show.href}&resource_hrefs[]=#{new_deployment2.show.href}&tags[]=tag1").should == nil
59
76
  tags = @client.tags.by_resource("resource_hrefs[]=#{deployment.show.href}&resource_hrefs[]=#{new_deployment2.show.href}")
60
77
  tags.class.should == Array
61
78
  tags.first.class.should == RightApi::ResourceDetail
@@ -65,5 +82,61 @@ describe RightApi::Client do
65
82
  deployment.destroy.should be_nil
66
83
  new_deployment2.destroy.should be_nil
67
84
  end
85
+
86
+ it "singularizes resource_types correctly" do
87
+ @client.get_singular('servers').should == 'server'
88
+ @client.get_singular('deployments').should == 'deployment'
89
+ @client.get_singular('audit_entries').should == 'audit_entry'
90
+ @client.get_singular('processes').should == 'process'
91
+ @client.get_singular('ip_addresses').should == 'ip_address'
92
+ end
93
+
94
+ it "returns the resource when calling #resource(href)" do
95
+
96
+ d0 = @client.deployments.index.first
97
+
98
+ d1 = @client.resource(d0.href)
99
+
100
+ d1.href.should == d0.href
101
+ end
102
+
103
+ it "raises meaningful errors" do
104
+
105
+ err = begin
106
+ @client.resource('/api/nada')
107
+ rescue => e
108
+ e
109
+ end
110
+
111
+ err.class.should ==
112
+ RightApi::UnknownRouteError
113
+ err.message.should ==
114
+ "Unknown action or route. HTTP Code: 404, Response body: " +
115
+ "NotFound: No route matches \"/api/nada\" with {:method=>:get}"
116
+ end
117
+
118
+ it "wraps errors with _details" do
119
+
120
+ err = begin
121
+ @client.deployments(:id => 'nada').show
122
+ rescue => e
123
+ e
124
+ end
125
+
126
+ #p err
127
+ #puts err.backtrace
128
+
129
+ err._details.method.should == :get
130
+ err._details.path.should == '/api/deployments/nada'
131
+ err._details.params.should == {}
132
+
133
+ err._details.request.class.should == RestClient::Request
134
+
135
+ err._details.response.code.should == 422
136
+ err._details.response.class.should == String
137
+ err._details.response.should == "ResourceNotFound: Couldn't find Deployment with ID=nada "
138
+
139
+ err._details.code.should == 422
140
+ end
68
141
  end
69
142
  end