right_cloud_api_base 0.1.0

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY +2 -0
  3. data/LICENSE +19 -0
  4. data/README.md +14 -0
  5. data/Rakefile +37 -0
  6. data/lib/base/api_manager.rb +707 -0
  7. data/lib/base/helpers/cloud_api_logger.rb +214 -0
  8. data/lib/base/helpers/http_headers.rb +239 -0
  9. data/lib/base/helpers/http_parent.rb +103 -0
  10. data/lib/base/helpers/http_request.rb +173 -0
  11. data/lib/base/helpers/http_response.rb +122 -0
  12. data/lib/base/helpers/net_http_patch.rb +31 -0
  13. data/lib/base/helpers/query_api_patterns.rb +862 -0
  14. data/lib/base/helpers/support.rb +270 -0
  15. data/lib/base/helpers/support.xml.rb +306 -0
  16. data/lib/base/helpers/utils.rb +380 -0
  17. data/lib/base/manager.rb +122 -0
  18. data/lib/base/parsers/json.rb +38 -0
  19. data/lib/base/parsers/plain.rb +36 -0
  20. data/lib/base/parsers/rexml.rb +83 -0
  21. data/lib/base/parsers/sax.rb +200 -0
  22. data/lib/base/routines/cache_validator.rb +184 -0
  23. data/lib/base/routines/connection_proxies/net_http_persistent_proxy.rb +194 -0
  24. data/lib/base/routines/connection_proxies/right_http_connection_proxy.rb +224 -0
  25. data/lib/base/routines/connection_proxy.rb +66 -0
  26. data/lib/base/routines/request_analyzer.rb +122 -0
  27. data/lib/base/routines/request_generator.rb +48 -0
  28. data/lib/base/routines/request_initializer.rb +52 -0
  29. data/lib/base/routines/response_analyzer.rb +152 -0
  30. data/lib/base/routines/response_parser.rb +79 -0
  31. data/lib/base/routines/result_wrapper.rb +75 -0
  32. data/lib/base/routines/retry_manager.rb +106 -0
  33. data/lib/base/routines/routine.rb +98 -0
  34. data/lib/right_cloud_api_base.rb +72 -0
  35. data/lib/right_cloud_api_base_version.rb +37 -0
  36. data/right_cloud_api_base.gemspec +63 -0
  37. data/spec/helpers/query_api_pattern_spec.rb +312 -0
  38. data/spec/helpers/support_spec.rb +211 -0
  39. data/spec/helpers/support_xml_spec.rb +207 -0
  40. data/spec/helpers/utils_spec.rb +179 -0
  41. data/spec/routines/connection_proxies/test_net_http_persistent_proxy_spec.rb +143 -0
  42. data/spec/routines/test_cache_validator_spec.rb +152 -0
  43. data/spec/routines/test_connection_proxy_spec.rb +44 -0
  44. data/spec/routines/test_request_analyzer_spec.rb +106 -0
  45. data/spec/routines/test_response_analyzer_spec.rb +132 -0
  46. data/spec/routines/test_response_parser_spec.rb +228 -0
  47. data/spec/routines/test_result_wrapper_spec.rb +63 -0
  48. data/spec/routines/test_retry_manager_spec.rb +84 -0
  49. data/spec/spec_helper.rb +15 -0
  50. metadata +215 -0
@@ -0,0 +1,173 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScale
25
+ module CloudApi
26
+
27
+ # A Wrapper around standard Net::HTTPRequest class.
28
+ #
29
+ # @api public
30
+ #
31
+ # The class supports some handy methods for managing the verb, the body, the path and the headers.
32
+ # And everythig else can be accessed through *raw* attribute that points to the original
33
+ # Net::HTTPRequest instance.
34
+ #
35
+ class HTTPRequest < HTTPParent
36
+
37
+
38
+ # Request HTTP verb
39
+ #
40
+ # @return [String]
41
+ # @example
42
+ # response.verb #=> 'get'
43
+ #
44
+ attr_accessor :verb
45
+
46
+
47
+ # Request path
48
+ #
49
+ # @return [String]
50
+ # @example
51
+ # response.path #=> 'xxx/yyy/zzz'
52
+ #
53
+ attr_accessor :path
54
+
55
+
56
+ # Request HTTP params
57
+ #
58
+ # @return [Hash]
59
+ # @example
60
+ # response.params #=> { 'a' => 'b', 'c' => 'd' }
61
+ #
62
+ attr_accessor :params
63
+
64
+
65
+ # Max byte to log
66
+ BODY_BYTES_TO_LOG = 6000
67
+
68
+
69
+ # Constructor
70
+ #
71
+ # @param [String,Symbol] verb The current verb ('get', 'post', 'put', etc).
72
+ # @param [String,IO,Nil] body The request body.
73
+ # @param [String] path The URL path.
74
+ # @param [Hash] headers The request headers.
75
+ # @param [Net::HTTPRequest] raw The original request (optional).
76
+ #
77
+ # @return [Rightscale::CloudApi::HTTPRequest] A new instance.
78
+ #
79
+ # @example
80
+ # new('get', 'xxx/yyy/zzz', nil, {})
81
+ #
82
+ def initialize(verb, path, body, headers, raw=nil)
83
+ # Create a request
84
+ @verb = verb.to_s.downcase
85
+ @path = path
86
+ @raws = raw
87
+ @headers = HTTPHeaders::new(headers)
88
+ self.body = body
89
+ end
90
+
91
+
92
+ # Sets a new headers value(s)
93
+ #
94
+ # @param [String] header The header name.
95
+ # @param [String, Array] value The value for the header.
96
+ # @return [void]
97
+ # @example
98
+ # # no example
99
+ #
100
+ def []=(header, value)
101
+ @headers[header] = value
102
+ end
103
+
104
+
105
+ # Sets the body and the 'content-length' header
106
+ #
107
+ # If the body is blank it sets the header to 0.
108
+ # If the body is a String it sets the header to the string size.
109
+ # If the body is an IO object it tries to open it in *binmode* mode and sets the header to
110
+ # the filesize (if the header is not set or points outside of the range of (0..filesize-1)).
111
+ #
112
+ # @param [Object] new_body
113
+ # @return [void]
114
+ # @example
115
+ # # no example
116
+ #
117
+ def body=(new_body)
118
+ # Set a request body
119
+ if new_body._blank?
120
+ @body = nil
121
+ self['content-length'] = 0
122
+ else
123
+ if new_body.is_a?(IO)
124
+ @body = file = new_body
125
+ # Make sure the file is openned in binmode
126
+ file.binmode if file.respond_to?(:binmode)
127
+ # Fix 'content-length': it must not be bigger than a piece of a File left to be read or a String body size.
128
+ # Otherwise the connection may behave like crazy causing 4xx or 5xx responses
129
+ # KD: Make sure this code is used with the patched RightHttpConnection gem (see net_fix.rb)
130
+ file_size = file.respond_to?(:lstat) ? file.lstat.size : file.size
131
+ bytes_to_read = [ file_size - file.pos, self['content-length'].first ].compact.map{|v| v.to_i }.sort.first # remove nils then make values Integers
132
+ if self['content-length'].first._blank? || self['content-length'].first.to_i > bytes_to_read
133
+ self['content-length'] = bytes_to_read
134
+ end
135
+ else
136
+ @body = new_body
137
+ self['content-length'] = body.size if self['content-length'].first.to_i > body.size
138
+ end
139
+ end
140
+ end
141
+
142
+
143
+ # Displays the request as a String with the verb and the path
144
+ #
145
+ # @return [String] The request verb and path info.
146
+ # @example
147
+ # ec2.request.to_s #=>
148
+ # "GET /?AWSAccessKeyId=000..000A&Action=DescribeSecurityGroups&SignatureMethod=HmacSHA256&
149
+ # SignatureVersion=2&Timestamp=2013-02-22T23%3A54%3A30.000Z&Version=2012-10-15&
150
+ # Signature=Gd...N4yQStO5aKXfYnrM4%3D"
151
+ #
152
+ def to_s
153
+ "#{verb.upcase} #{path}"
154
+ end
155
+
156
+
157
+ # Displays the body information
158
+ #
159
+ # @return [String] The body info.
160
+ # @example
161
+ # request.body_info #=> "something"
162
+ #
163
+ def body_info
164
+ if is_io?
165
+ "#{body.class.name}, size: #{body.respond_to?(:lstat) ? body.lstat.size : body.size}, pos: #{body.pos}"
166
+ else
167
+ "size: #{body.to_s.size}, first #{BODY_BYTES_TO_LOG} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG]}"
168
+ end
169
+ end
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,122 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScale
25
+ module CloudApi
26
+
27
+ # A Wrapper around standard Net::HTTPRsponse class
28
+ #
29
+ # @api public
30
+ #
31
+ # The class supports some handy methods for managing the code, the body and the headers.
32
+ # And everythig else can be accessed through *raw* attribute that points to the original
33
+ # Net::HTTPResponse instance.
34
+ #
35
+ class HTTPResponse < HTTPParent
36
+
37
+ # The response code
38
+ #
39
+ # @return [String]
40
+ # @example
41
+ # response.code #=> '404'
42
+ #
43
+ attr_reader :code
44
+
45
+ # Body bytes to log
46
+ BODY_BYTES_TO_LOG = 2000
47
+
48
+ # Body bytes to log in case of error
49
+ BODY_BYTES_TO_LOG_ERROR = 6000
50
+
51
+
52
+ # Constructor
53
+ #
54
+ # @param [String] code The http response code.
55
+ # @param [String,IO,Nil] body The response body.
56
+ # @param [Hash] headers The response headers.
57
+ # @param [Net::HTTPRequest] raw The original response (optional).
58
+ #
59
+ # @return [Rightscale::CloudApi::HTTPResponse] A new response instance.
60
+ #
61
+ # @example
62
+ # new('200', 'body', {}, object)
63
+ #
64
+ def initialize(code, body, headers, raw)
65
+ @code = code.to_s
66
+ @body = body
67
+ @raw = raw
68
+ @headers = HTTPHeaders::new(headers)
69
+ end
70
+
71
+
72
+ # Returns true if the response code is in the range of 4xx or 5xx
73
+ #
74
+ # @return [Boolean]
75
+ # @example
76
+ # response.is_error? #=> false
77
+ #
78
+ def is_error?
79
+ !!(code.is_a?(String) && code.match(/^(5..|4..)/))
80
+ end
81
+
82
+
83
+ # Returns true if the response code is in the range of 3xx
84
+ #
85
+ # @return [Boolean]
86
+ # @example
87
+ # response.is_redirect? #=> false
88
+ #
89
+ def is_redirect?
90
+ !!(code.is_a?(String) && code.match(/^3..$/))
91
+ end
92
+
93
+
94
+ # Returns the response code and code name
95
+ #
96
+ # @return [String]
97
+ # @example
98
+ # ec2.response.to_s #=> '200 OK'
99
+ #
100
+ def to_s
101
+ result = code.dup
102
+ result << " #{raw.class.name[/Net::HTTP(.*)/] && $1}" if raw.is_a?(Net::HTTPResponse)
103
+ result
104
+ end
105
+
106
+
107
+ # Displays the body information
108
+ #
109
+ # @return [String] The body info.
110
+ # @example
111
+ # ec2.response.body_info #=> 'response boby'
112
+ #
113
+ def body_info
114
+ if is_io? then "#{body.class.name}"
115
+ elsif is_error? then "size: #{body.to_s.size}, first #{BODY_BYTES_TO_LOG_ERROR} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG_ERROR]}"
116
+ else "size: #{body.to_s.size}, first #{BODY_BYTES_TO_LOG} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG]}"
117
+ end
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,31 @@
1
+ #--
2
+ # Copyright (c) 2013 RightScale, Inc.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # 'Software'), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ # TBD: Remove once we use ruby 1.9.3 which introduces this class
25
+ unless defined?(Net::HTTP::Patch)
26
+ class Net::HTTP::Patch < Net::HTTPRequest
27
+ METHOD = 'PATCH'
28
+ REQUEST_HAS_BODY = true
29
+ RESPONSE_HAS_BODY = true
30
+ end
31
+ end