simple_aws 0.0.1b → 0.0.1c
Sign up to get free protection for your applications and to get access to all the features.
- 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
|