simple_aws 0.0.1b → 0.0.1c
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.
- data/README.md +1 -1
- data/lib/aws/core/connection.rb +4 -2
- data/lib/aws/core/request.rb +7 -0
- data/lib/aws/core/response.rb +47 -15
- data/lib/aws/s3.rb +127 -0
- data/lib/aws/signing/authorization_header.rb +43 -0
- data/samples/s3.rb +64 -0
- data/simple_aws.gemspec +2 -1
- data/test/aws/core/connection_test.rb +12 -0
- data/test/aws/core/request_test.rb +16 -7
- data/test/aws/core/response_test.rb +78 -1
- data/test/aws/s3_test.rb +108 -0
- data/test/aws/signing/authorization_header_test.rb +33 -0
- metadata +24 -6
data/README.md
CHANGED
@@ -106,6 +106,7 @@ Implemented APIs
|
|
106
106
|
|
107
107
|
These are the following Amazon APIs that SimpleAWS currently handles:
|
108
108
|
|
109
|
+
* S3
|
109
110
|
* EC2
|
110
111
|
* ELB
|
111
112
|
* IAM
|
@@ -125,7 +126,6 @@ These are the following Amazon APIs that SimpleAWS currently handles:
|
|
125
126
|
Yet to be Implemented
|
126
127
|
---------------------
|
127
128
|
|
128
|
-
* S3
|
129
129
|
* CloudFront
|
130
130
|
|
131
131
|
Currently Out-Of-Scope
|
data/lib/aws/core/connection.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'httparty'
|
2
|
+
require 'httmultiparty'
|
2
3
|
require 'openssl'
|
3
4
|
|
4
5
|
require 'aws/core/response'
|
@@ -6,7 +7,7 @@ require 'aws/core/response'
|
|
6
7
|
module AWS
|
7
8
|
|
8
9
|
class HTTP
|
9
|
-
include
|
10
|
+
include HTTMultiParty
|
10
11
|
format :xml
|
11
12
|
end
|
12
13
|
|
@@ -24,7 +25,8 @@ module AWS
|
|
24
25
|
HTTP.send(request.method,
|
25
26
|
request.uri,
|
26
27
|
:query => request.params,
|
27
|
-
:headers => request.headers
|
28
|
+
:headers => request.headers,
|
29
|
+
:body => request.body
|
28
30
|
)
|
29
31
|
)
|
30
32
|
end
|
data/lib/aws/core/request.rb
CHANGED
@@ -111,6 +111,13 @@ module AWS
|
|
111
111
|
##
|
112
112
|
attr_reader :headers
|
113
113
|
|
114
|
+
##
|
115
|
+
# Raw string data to be put in the body of the request.
|
116
|
+
# Body can also be an IO object (something that response to #read) and
|
117
|
+
# if so the request will stream the file to the server.
|
118
|
+
##
|
119
|
+
attr_accessor :body
|
120
|
+
|
114
121
|
##
|
115
122
|
# Set up a new Request for the given +host+ and +path+ using the given
|
116
123
|
# http +method+ (:get, :post, :put, :delete).
|
data/lib/aws/core/response.rb
CHANGED
@@ -146,8 +146,11 @@ module AWS
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def value_or_proxy(value)
|
149
|
-
|
149
|
+
case value
|
150
|
+
when Hash
|
150
151
|
ResponseProxy.new value
|
152
|
+
when Array
|
153
|
+
value.map {|v| ResponseProxy.new v }
|
151
154
|
else
|
152
155
|
value
|
153
156
|
end
|
@@ -159,27 +162,36 @@ module AWS
|
|
159
162
|
##
|
160
163
|
attr_reader :body
|
161
164
|
|
165
|
+
##
|
166
|
+
# HTTP Status code of the response
|
167
|
+
##
|
168
|
+
attr_reader :code
|
169
|
+
|
170
|
+
##
|
171
|
+
# Hash of headers found in the response
|
172
|
+
##
|
173
|
+
attr_reader :headers
|
174
|
+
|
162
175
|
def initialize(http_response)
|
163
176
|
if !http_response.success?
|
164
|
-
|
165
|
-
raise UnsuccessfulResponse.new(
|
166
|
-
http_response.code,
|
167
|
-
error["Code"],
|
168
|
-
error["Message"]
|
169
|
-
)
|
177
|
+
parse_and_throw_error_from http_response
|
170
178
|
end
|
171
179
|
|
180
|
+
@code = http_response.code
|
172
181
|
@body = http_response.parsed_response
|
182
|
+
@headers = http_response.headers
|
173
183
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
inner
|
178
|
-
|
179
|
-
|
180
|
-
|
184
|
+
if @body
|
185
|
+
inner = @body[@body.keys.first]
|
186
|
+
response_root =
|
187
|
+
if result_key = inner.keys.find {|k| k =~ /Result$/}
|
188
|
+
inner[result_key]
|
189
|
+
else
|
190
|
+
inner
|
191
|
+
end
|
181
192
|
|
182
|
-
|
193
|
+
@request_root = ResponseProxy.new response_root
|
194
|
+
end
|
183
195
|
end
|
184
196
|
|
185
197
|
##
|
@@ -216,9 +228,29 @@ module AWS
|
|
216
228
|
|
217
229
|
protected
|
218
230
|
|
231
|
+
def parse_and_throw_error_from(http_response)
|
232
|
+
if http_response.parsed_response
|
233
|
+
error = parse_error_from http_response.parsed_response
|
234
|
+
else
|
235
|
+
error = { "Message" => http_response.response }
|
236
|
+
end
|
237
|
+
|
238
|
+
raise UnsuccessfulResponse.new(
|
239
|
+
http_response.code,
|
240
|
+
error["Code"],
|
241
|
+
error["Message"]
|
242
|
+
)
|
243
|
+
end
|
244
|
+
|
219
245
|
def parse_error_from(body)
|
220
246
|
if body.has_key? "ErrorResponse"
|
221
247
|
body["ErrorResponse"]["Error"]
|
248
|
+
elsif body.has_key? "Error"
|
249
|
+
if body["Error"]["StringToSign"]
|
250
|
+
body["Error"]["Message"] += " String to Sign: #{body["Error"]["StringToSign"].inspect}"
|
251
|
+
end
|
252
|
+
|
253
|
+
body["Error"]
|
222
254
|
elsif body.has_key? "Response"
|
223
255
|
body["Response"]["Errors"]["Error"]
|
224
256
|
else
|
data/lib/aws/s3.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'aws/api'
|
2
|
+
require 'aws/signing/authorization_header'
|
3
|
+
|
4
|
+
module AWS
|
5
|
+
|
6
|
+
##
|
7
|
+
# Amazon's Simple Storage Service
|
8
|
+
#
|
9
|
+
# http://docs.amazonwebservices.com/AmazonS3/latest/API/Welcome.html
|
10
|
+
#
|
11
|
+
# As S3 is much closer to a RESTful service than the other AWS APIs, all
|
12
|
+
# calls through this API are done through the four handled HTTP METHODS:
|
13
|
+
# GET, PUT, DELETE, POST and HEAD. When sending a request, follow exactly what
|
14
|
+
# is described in the AWS API docs in the link above.
|
15
|
+
#
|
16
|
+
# So "GET Service" is
|
17
|
+
#
|
18
|
+
# s3.get "/"
|
19
|
+
#
|
20
|
+
# When working with a specific bucket, pass in :bucket after the path:
|
21
|
+
#
|
22
|
+
# s3.get "/", :bucket => "bucket_name"
|
23
|
+
#
|
24
|
+
# s3.get "/?policy", :bucket => "bucket_name"
|
25
|
+
#
|
26
|
+
# For requests that need extra parameters, use the :params option:
|
27
|
+
#
|
28
|
+
# s3.get "/object/name", :bucket => "bucket_name", :params => {
|
29
|
+
# "response-content-disposition" => "attachment"
|
30
|
+
# }
|
31
|
+
#
|
32
|
+
# Also use params in the cases that AWS asks for form fields, such as
|
33
|
+
# "POST Object".
|
34
|
+
#
|
35
|
+
# A lot of S3 communication happens through request and response headers.
|
36
|
+
# To specify a certian set of headers on the request, use :headers:
|
37
|
+
#
|
38
|
+
# s3.get "/", :bucket => "bucket_name", :headers => {
|
39
|
+
# "x-amz-security-token" => "security string"
|
40
|
+
# }
|
41
|
+
#
|
42
|
+
# Many of the PUT requests require a body of some sort, sometimes XML,
|
43
|
+
# sometimes JSON, and other times the raw file data. Use :body for this
|
44
|
+
# information. :body is expected to be either a String containing the XML or
|
45
|
+
# JSON information, or an object that otherwise response to #read for file
|
46
|
+
# uploads. This API does not build XML or JSON for you right now.
|
47
|
+
#
|
48
|
+
# s3.put "/object/name.txt", :bucket => "bucket_name", :body => {:file => File.open()}
|
49
|
+
#
|
50
|
+
# As this is a common use case, if you don't have any more parameters to send
|
51
|
+
# with the file being uploaded, just use :file.
|
52
|
+
#
|
53
|
+
# s3.put "/object/name.txt", :bucket => "bucket_name", :file => File.open()
|
54
|
+
#
|
55
|
+
# This API does ensure that file data is uploaded as efficiently as possible,
|
56
|
+
# streaming file data from disc to AWS without blowing up memory. All files are
|
57
|
+
# uploading using multipart/form-data.
|
58
|
+
#
|
59
|
+
# NOTE: Like the other parts of SimpleAWS, this API does NOT try to make the
|
60
|
+
# AWS API better, but simply provides a cleaner, easy to use API for Ruby.
|
61
|
+
# As such, this API does not offer streaming downloads of file data from S3.
|
62
|
+
# That is up to you to implement at this time, by running a HEAD to get
|
63
|
+
# Content-Length then repeated GETs using the "Range:bytes" header to specify
|
64
|
+
# which parts to download next.
|
65
|
+
#
|
66
|
+
# Raw file data in a response will be available in the #body method on the Response
|
67
|
+
# returned by the method call.
|
68
|
+
##
|
69
|
+
class S3 < API
|
70
|
+
endpoint "s3"
|
71
|
+
use_https true
|
72
|
+
version "2006-03-01"
|
73
|
+
|
74
|
+
[:get, :post, :put, :delete, :head].each do |method|
|
75
|
+
define_method(method) do |*args|
|
76
|
+
self.call method, *args
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def call(method, path, options = {})
|
81
|
+
if options[:bucket]
|
82
|
+
path = path.gsub(/^\//, "/#{options[:bucket]}/")
|
83
|
+
end
|
84
|
+
|
85
|
+
request = AWS::Request.new method, self.uri, path
|
86
|
+
|
87
|
+
(options[:params] || {}).each do |k, v|
|
88
|
+
request.params[k] = v
|
89
|
+
end
|
90
|
+
|
91
|
+
(options[:headers] || {}).each do |k, v|
|
92
|
+
request.headers[k] = v
|
93
|
+
end
|
94
|
+
|
95
|
+
if options[:file]
|
96
|
+
options[:body] = {:file => options[:file]}
|
97
|
+
end
|
98
|
+
|
99
|
+
request.body = options[:body]
|
100
|
+
|
101
|
+
if request.body.is_a?(Hash) && request.body[:file]
|
102
|
+
request.headers["Content-Type"] =
|
103
|
+
"multipart/form-data; boundary=-----------RubyMultipartPost"
|
104
|
+
end
|
105
|
+
|
106
|
+
connection = AWS::Connection.new
|
107
|
+
connection.call finish_and_sign_request(request)
|
108
|
+
end
|
109
|
+
|
110
|
+
include Signing::AuthorizationHeader
|
111
|
+
|
112
|
+
##
|
113
|
+
# S3 handles region endpoints a little differently
|
114
|
+
##
|
115
|
+
def uri
|
116
|
+
return @uri if @uri
|
117
|
+
|
118
|
+
@uri = @use_https ? "https" : "http"
|
119
|
+
@uri += "://#{@endpoint}"
|
120
|
+
@uri += "-#{@region}" if @region
|
121
|
+
@uri += ".amazonaws.com"
|
122
|
+
@uri
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module AWS
|
4
|
+
module Signing
|
5
|
+
##
|
6
|
+
# Implementation of signing using the Authorization header, as used by S3 and CloudFront
|
7
|
+
##
|
8
|
+
module AuthorizationHeader
|
9
|
+
|
10
|
+
##
|
11
|
+
# Build and sign the final request, as per the rules here:
|
12
|
+
# http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
|
13
|
+
##
|
14
|
+
def finish_and_sign_request(request)
|
15
|
+
request.headers["Date"] = Time.now.utc.httpdate
|
16
|
+
request.headers["Authorization"] =
|
17
|
+
"AWS #{self.access_key}:#{Base64.encode64(build_signature_for(request)).chomp}"
|
18
|
+
|
19
|
+
request
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_signature_for(request)
|
23
|
+
amazon_headers = request.headers.select {|k, v|
|
24
|
+
k =~ /^x-amz/i
|
25
|
+
}.map {|k, v|
|
26
|
+
"#{k.downcase}:#{v}".chomp
|
27
|
+
}
|
28
|
+
|
29
|
+
to_sign = [
|
30
|
+
request.method.to_s.upcase,
|
31
|
+
request.headers["Content-Md5"] || "",
|
32
|
+
request.headers["Content-Type"] || "",
|
33
|
+
request.headers["Date"],
|
34
|
+
amazon_headers,
|
35
|
+
request.path
|
36
|
+
].flatten.join("\n")
|
37
|
+
|
38
|
+
OpenSSL::HMAC.digest("sha1", self.secret_key, to_sign)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/samples/s3.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
$: << File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'aws/s3'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Expects your Amazon keys to be in the environment, something like
|
7
|
+
#
|
8
|
+
# export AWS_KEY="KEY"
|
9
|
+
# export AWS_SECRET="SECRET"
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
# ruby sample/s3.rb [bucket name] [file to use]
|
13
|
+
#
|
14
|
+
# This script will upload the file, show that the file is up, then remove the file
|
15
|
+
##
|
16
|
+
|
17
|
+
def bad_usage
|
18
|
+
puts ""
|
19
|
+
puts "Usage:"
|
20
|
+
puts " ruby sample/s3.rb [bucket name] [file to use]"
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
s3 = AWS::S3.new ENV["AWS_KEY"], ENV["AWS_SECRET"]
|
25
|
+
|
26
|
+
bucket_name = ARGV[0]
|
27
|
+
file_name = ARGV[1]
|
28
|
+
|
29
|
+
puts "All buckets in this account:", ""
|
30
|
+
|
31
|
+
s3.get("/").buckets.bucket.each do |bucket|
|
32
|
+
puts bucket.name
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "", "First 10 files in #{bucket_name}:", ""
|
36
|
+
|
37
|
+
bad_usage unless bucket_name
|
38
|
+
|
39
|
+
s3.get("/", :bucket => bucket_name, :params => {"max-keys" => 10}).contents.each do |entry|
|
40
|
+
puts entry.key
|
41
|
+
end
|
42
|
+
|
43
|
+
puts "", "Uploading #{file_name} to #{bucket_name}:", ""
|
44
|
+
|
45
|
+
bad_usage unless file_name
|
46
|
+
uploaded_file_name = File.basename file_name
|
47
|
+
|
48
|
+
p s3.put("/#{uploaded_file_name}", :bucket => bucket_name, :file => File.open(file_name))
|
49
|
+
|
50
|
+
puts "", "Checking that the file now exists...", ""
|
51
|
+
|
52
|
+
p s3.head("/#{uploaded_file_name}", :bucket => bucket_name)
|
53
|
+
|
54
|
+
puts "", "Deleting the file from S3", ""
|
55
|
+
|
56
|
+
p s3.delete("/#{uploaded_file_name}", :bucket => bucket_name)
|
57
|
+
|
58
|
+
puts "", "Checking that file is no longer in S3...", ""
|
59
|
+
|
60
|
+
begin
|
61
|
+
p s3.head("/#{uploaded_file_name}", :bucket => bucket_name)
|
62
|
+
rescue => ex
|
63
|
+
puts "Not found: #{ex.message}"
|
64
|
+
end
|
data/simple_aws.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "simple_aws"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.1c"
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.authors = ["Jason Roelofs"]
|
6
6
|
s.email = ["jameskilton@gmail.com"]
|
@@ -12,6 +12,7 @@ Gem::Specification.new do |s|
|
|
12
12
|
|
13
13
|
s.add_dependency "ox"
|
14
14
|
s.add_dependency "httparty"
|
15
|
+
s.add_dependency "httmultiparty"
|
15
16
|
|
16
17
|
s.add_dependency "jruby-openssl" if RUBY_PLATFORM == 'java'
|
17
18
|
|
@@ -56,5 +56,17 @@ describe AWS::Connection do
|
|
56
56
|
@connection.call request
|
57
57
|
end
|
58
58
|
|
59
|
+
it "passes through any body on the Request" do
|
60
|
+
request = AWS::Request.new(:get, "host.com", "/")
|
61
|
+
request.body = "This is some body text"
|
62
|
+
|
63
|
+
AWS::HTTP.expects(:get).with {|uri, options|
|
64
|
+
options[:query].wont_be_nil
|
65
|
+
options[:body].must_equal "This is some body text"
|
66
|
+
}.returns(@http_response)
|
67
|
+
|
68
|
+
@connection.call request
|
69
|
+
end
|
70
|
+
|
59
71
|
end
|
60
72
|
end
|
@@ -19,13 +19,6 @@ describe AWS::Request do
|
|
19
19
|
@request.method.must_equal :get
|
20
20
|
end
|
21
21
|
|
22
|
-
it "can be given parameters to pass in" do
|
23
|
-
@request.params["Param1"] = "Value1"
|
24
|
-
@request.params["Param2"] = "Value2"
|
25
|
-
|
26
|
-
@request.params.must_equal "Param1" => "Value1", "Param2" => "Value2"
|
27
|
-
end
|
28
|
-
|
29
22
|
it "ensures path is never an empty string" do
|
30
23
|
@request.path = ""
|
31
24
|
@request.path.must_equal "/"
|
@@ -38,6 +31,22 @@ describe AWS::Request do
|
|
38
31
|
end
|
39
32
|
end
|
40
33
|
|
34
|
+
describe "parameters" do
|
35
|
+
it "can be given parameters to pass in" do
|
36
|
+
@request.params["Param1"] = "Value1"
|
37
|
+
@request.params["Param2"] = "Value2"
|
38
|
+
|
39
|
+
@request.params.must_equal "Param1" => "Value1", "Param2" => "Value2"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "body" do
|
44
|
+
it "can be given raw body text" do
|
45
|
+
@request.body = "BODY!"
|
46
|
+
@request.body.must_equal "BODY!"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
41
50
|
describe "hashes" do
|
42
51
|
it "converts hash params to AWS param names" do
|
43
52
|
@request.params["Filter"] = [
|
@@ -8,6 +8,7 @@ describe AWS::Response do
|
|
8
8
|
before do
|
9
9
|
@error_response = {}
|
10
10
|
@http_response = stub
|
11
|
+
@http_response.stubs(:headers).returns(nil)
|
11
12
|
@http_response.stubs(:success?).returns(false)
|
12
13
|
@http_response.stubs(:code).returns(401)
|
13
14
|
end
|
@@ -55,6 +56,25 @@ describe AWS::Response do
|
|
55
56
|
error.message.must_equal "AuthFailure (401): Message about failing to authenticate"
|
56
57
|
end
|
57
58
|
|
59
|
+
it "handles S3 errors" do
|
60
|
+
@error_response = { "Error" => {
|
61
|
+
"Code" => "AuthFailure", "Message" => "OH snap!",
|
62
|
+
"StringToSign" => "SOME STRING"
|
63
|
+
} }
|
64
|
+
@http_response.stubs(:parsed_response).returns(@error_response)
|
65
|
+
|
66
|
+
error =
|
67
|
+
lambda {
|
68
|
+
AWS::Response.new @http_response
|
69
|
+
}.must_raise AWS::UnsuccessfulResponse
|
70
|
+
|
71
|
+
error.code.must_equal 401
|
72
|
+
error.error_type.must_equal "AuthFailure"
|
73
|
+
error.error_message.must_equal "OH snap! String to Sign: \"SOME STRING\""
|
74
|
+
|
75
|
+
error.message.must_equal "AuthFailure (401): OH snap! String to Sign: \"SOME STRING\""
|
76
|
+
end
|
77
|
+
|
58
78
|
it "raises if it can't parse the error message" do
|
59
79
|
@error_response = { "Erroring" => "This is an error message" }
|
60
80
|
@http_response.stubs(:parsed_response).returns(@error_response)
|
@@ -66,6 +86,19 @@ describe AWS::Response do
|
|
66
86
|
|
67
87
|
error.message.must_match /Unable to parse error code from/
|
68
88
|
end
|
89
|
+
|
90
|
+
it "handles errors that have no body to parse" do
|
91
|
+
@http_response.stubs(:code).returns(404)
|
92
|
+
@http_response.stubs(:parsed_response).returns(nil)
|
93
|
+
@http_response.stubs(:response).returns("This is a response ok?")
|
94
|
+
|
95
|
+
error = lambda {
|
96
|
+
response = AWS::Response.new @http_response
|
97
|
+
}.must_raise AWS::UnsuccessfulResponse
|
98
|
+
|
99
|
+
error.code.must_equal 404
|
100
|
+
error.message.must_equal " (404): This is a response ok?"
|
101
|
+
end
|
69
102
|
end
|
70
103
|
|
71
104
|
describe "successful response parsing and mapping" do
|
@@ -79,6 +112,8 @@ describe AWS::Response do
|
|
79
112
|
}
|
80
113
|
|
81
114
|
@http_response = stub
|
115
|
+
@http_response.stubs(:headers).returns(nil)
|
116
|
+
@http_response.stubs(:code).returns(200)
|
82
117
|
@http_response.stubs(:success?).returns(true)
|
83
118
|
@http_response.stubs(:parsed_response).returns(@response_hash)
|
84
119
|
|
@@ -104,6 +139,23 @@ describe AWS::Response do
|
|
104
139
|
@response["unknownKey"].must_be_nil
|
105
140
|
end
|
106
141
|
|
142
|
+
it "handles responses with no body" do
|
143
|
+
@http_response.stubs(:parsed_response).returns(nil)
|
144
|
+
response = AWS::Response.new @http_response
|
145
|
+
response.body.must_be_nil
|
146
|
+
end
|
147
|
+
|
148
|
+
it "knows the status code of the response" do
|
149
|
+
@response.code.must_equal 200
|
150
|
+
end
|
151
|
+
|
152
|
+
it "pulls out any response headers" do
|
153
|
+
@http_response.stubs(:headers).returns({"Header1" => "Value2"})
|
154
|
+
response = AWS::Response.new @http_response
|
155
|
+
|
156
|
+
response.headers.must_equal "Header1" => "Value2"
|
157
|
+
end
|
158
|
+
|
107
159
|
end
|
108
160
|
|
109
161
|
describe "deeply nested response/results objects" do
|
@@ -119,6 +171,8 @@ describe AWS::Response do
|
|
119
171
|
}
|
120
172
|
|
121
173
|
@http_response = stub
|
174
|
+
@http_response.stubs(:headers).returns(nil)
|
175
|
+
@http_response.stubs(:code).returns(202)
|
122
176
|
@http_response.stubs(:success?).returns(true)
|
123
177
|
@http_response.stubs(:parsed_response).returns(@response_hash)
|
124
178
|
|
@@ -167,11 +221,20 @@ describe AWS::Response do
|
|
167
221
|
"withMemberSet" => {
|
168
222
|
"member" => {"keyId" => "4567"}
|
169
223
|
},
|
170
|
-
"UpperCamelKey" => "purple dog"
|
224
|
+
"UpperCamelKey" => "purple dog",
|
225
|
+
"Buckets" => {
|
226
|
+
"Bucket" => [
|
227
|
+
{"Name" => "bucket1"},
|
228
|
+
{"Name" => "bucket2"},
|
229
|
+
{"Name" => "bucket3"}
|
230
|
+
]
|
231
|
+
}
|
171
232
|
}
|
172
233
|
}
|
173
234
|
|
174
235
|
@http_response = stub
|
236
|
+
@http_response.stubs(:code).returns(202)
|
237
|
+
@http_response.stubs(:headers).returns(nil)
|
175
238
|
@http_response.stubs(:success?).returns(true)
|
176
239
|
@http_response.stubs(:parsed_response).returns(@response_hash)
|
177
240
|
|
@@ -226,6 +289,12 @@ describe AWS::Response do
|
|
226
289
|
@response.upper_camel_key.must_equal "purple dog"
|
227
290
|
end
|
228
291
|
|
292
|
+
it "works with straight array responses" do
|
293
|
+
@response.buckets.bucket[0].name.must_equal "bucket1"
|
294
|
+
@response.buckets.bucket[1].name.must_equal "bucket2"
|
295
|
+
@response.buckets.bucket[2].name.must_equal "bucket3"
|
296
|
+
end
|
297
|
+
|
229
298
|
end
|
230
299
|
|
231
300
|
describe "hash keys" do
|
@@ -259,6 +328,12 @@ describe AWS::Response do
|
|
259
328
|
it "also squashes the 'member' tag" do
|
260
329
|
@response["withMemberSet"][0]["keyId"].must_equal "4567"
|
261
330
|
end
|
331
|
+
|
332
|
+
it "works with straight array responses" do
|
333
|
+
@response["Buckets"]["Bucket"][0]["Name"].must_equal "bucket1"
|
334
|
+
@response["Buckets"]["Bucket"][1]["Name"].must_equal "bucket2"
|
335
|
+
@response["Buckets"]["Bucket"][2]["Name"].must_equal "bucket3"
|
336
|
+
end
|
262
337
|
end
|
263
338
|
|
264
339
|
end
|
@@ -266,6 +341,8 @@ describe AWS::Response do
|
|
266
341
|
describe "#request_id" do
|
267
342
|
before do
|
268
343
|
@http_response = stub
|
344
|
+
@http_response.stubs(:headers).returns(nil)
|
345
|
+
@http_response.stubs(:code).returns(200)
|
269
346
|
@http_response.stubs(:success?).returns(true)
|
270
347
|
end
|
271
348
|
|
data/test/aws/s3_test.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'aws/s3'
|
3
|
+
|
4
|
+
describe AWS::S3 do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@api = AWS::S3.new "key", "secret"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "points to the endpoint" do
|
11
|
+
@api.uri.must_equal "https://s3.amazonaws.com"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "properly builds region endpoints" do
|
15
|
+
api = AWS::S3.new "key", "secret", "us-west-1"
|
16
|
+
api.uri.must_equal "https://s3-us-west-1.amazonaws.com"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "works with the current version" do
|
20
|
+
@api.version.must_equal "2006-03-01"
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "API calls" do
|
24
|
+
|
25
|
+
[:get, :post, :put, :delete, :head].each do |method|
|
26
|
+
it "supports the #{method} HTTP method" do
|
27
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
28
|
+
request.method.must_equal method
|
29
|
+
request.host.must_equal "https://s3.amazonaws.com"
|
30
|
+
request.path.must_equal "/"
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
@api.send method, "/"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "rebuilds the host if :bucket given" do
|
39
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
40
|
+
request.path.must_equal "/bucket-name/"
|
41
|
+
request.host.must_equal "https://s3.amazonaws.com"
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
@api.get "/", :bucket => "bucket-name"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "takes parameters" do
|
49
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
50
|
+
request.params["Parameter1"].must_equal "Value2"
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
@api.get "/", :params => { "Parameter1" => "Value2" }
|
55
|
+
end
|
56
|
+
|
57
|
+
it "takes extra headers" do
|
58
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
59
|
+
request.headers["Header14"].must_equal "Out to Lunch"
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
@api.get "/", :headers => { "Header14" => "Out to Lunch" }
|
64
|
+
end
|
65
|
+
|
66
|
+
it "takes a raw body" do
|
67
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
68
|
+
request.body.must_equal "This is a body of text"
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
@api.get "/", :body => "This is a body of text"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "adds the form-data header if the body has a file in it" do
|
76
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
77
|
+
request.body.must_equal :file => "This is a body of text"
|
78
|
+
request.headers["Content-Type"].must_equal(
|
79
|
+
"multipart/form-data; boundary=-----------RubyMultipartPost"
|
80
|
+
)
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
@api.get "/", :body => {:file => "This is a body of text"}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "allows use of :file if :body only contains a file" do
|
88
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
89
|
+
request.body.must_equal :file => "This is a body of text"
|
90
|
+
request.headers["Content-Type"].must_equal(
|
91
|
+
"multipart/form-data; boundary=-----------RubyMultipartPost"
|
92
|
+
)
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
@api.get "/", :file => "This is a body of text"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "signs the request using the Authorization header" do
|
100
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
101
|
+
request.headers["Authorization"].wont_be_nil
|
102
|
+
true
|
103
|
+
end
|
104
|
+
|
105
|
+
@api.get "/"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'aws/api'
|
3
|
+
require 'aws/call_types/action_param'
|
4
|
+
require 'aws/signing/authorization_header'
|
5
|
+
|
6
|
+
describe AWS::Signing::AuthorizationHeader do
|
7
|
+
|
8
|
+
class SigningAuthHeaderTestAPI < AWS::API
|
9
|
+
endpoint "aptest"
|
10
|
+
version "2011-01-01"
|
11
|
+
use_https true
|
12
|
+
|
13
|
+
include AWS::CallTypes::ActionParam
|
14
|
+
include AWS::Signing::AuthorizationHeader
|
15
|
+
end
|
16
|
+
|
17
|
+
it "signs the given request according to Version 3 rules" do
|
18
|
+
AWS::Connection.any_instance.expects(:call).with do |request|
|
19
|
+
header = request.headers["Authorization"]
|
20
|
+
parts = header.split(":")
|
21
|
+
|
22
|
+
parts[0].must_equal "AWS key"
|
23
|
+
parts[1].wont_be_nil
|
24
|
+
|
25
|
+
Time.parse(request.headers["Date"]).wont_be_nil
|
26
|
+
true
|
27
|
+
end.returns
|
28
|
+
|
29
|
+
obj = SigningAuthHeaderTestAPI.new "key", "secret"
|
30
|
+
obj.describe_instances
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_aws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1c
|
5
5
|
prerelease: 5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-01-
|
12
|
+
date: 2012-01-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ox
|
16
|
-
requirement: &
|
16
|
+
requirement: &70146086124160 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70146086124160
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: httparty
|
27
|
-
requirement: &
|
27
|
+
requirement: &70146086123740 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,7 +32,18 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70146086123740
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: httmultiparty
|
38
|
+
requirement: &70146086123280 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70146086123280
|
36
47
|
description: The simplest and easiest to use and maintain AWS communication library
|
37
48
|
email:
|
38
49
|
- jameskilton@gmail.com
|
@@ -63,7 +74,9 @@ files:
|
|
63
74
|
- lib/aws/map_reduce.rb
|
64
75
|
- lib/aws/mechanical_turk.rb
|
65
76
|
- lib/aws/rds.rb
|
77
|
+
- lib/aws/s3.rb
|
66
78
|
- lib/aws/ses.rb
|
79
|
+
- lib/aws/signing/authorization_header.rb
|
67
80
|
- lib/aws/signing/version2.rb
|
68
81
|
- lib/aws/signing/version3.rb
|
69
82
|
- lib/aws/sns.rb
|
@@ -71,6 +84,7 @@ files:
|
|
71
84
|
- samples/ec2.rb
|
72
85
|
- samples/elb.rb
|
73
86
|
- samples/iam.rb
|
87
|
+
- samples/s3.rb
|
74
88
|
- samples/sqs.rb
|
75
89
|
- samples/turk.rb
|
76
90
|
- simple_aws.gemspec
|
@@ -91,7 +105,9 @@ files:
|
|
91
105
|
- test/aws/map_reduce_test.rb
|
92
106
|
- test/aws/mechanical_turk_test.rb
|
93
107
|
- test/aws/rds_test.rb
|
108
|
+
- test/aws/s3_test.rb
|
94
109
|
- test/aws/ses.rb
|
110
|
+
- test/aws/signing/authorization_header_test.rb
|
95
111
|
- test/aws/signing/version2_test.rb
|
96
112
|
- test/aws/signing/version3.rb
|
97
113
|
- test/aws/sns_test.rb
|
@@ -139,7 +155,9 @@ test_files:
|
|
139
155
|
- test/aws/map_reduce_test.rb
|
140
156
|
- test/aws/mechanical_turk_test.rb
|
141
157
|
- test/aws/rds_test.rb
|
158
|
+
- test/aws/s3_test.rb
|
142
159
|
- test/aws/ses.rb
|
160
|
+
- test/aws/signing/authorization_header_test.rb
|
143
161
|
- test/aws/signing/version2_test.rb
|
144
162
|
- test/aws/signing/version3.rb
|
145
163
|
- test/aws/sns_test.rb
|