right_api_client 1.5.9 → 1.5.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -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