dynamodb 0.0.2 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,6 +31,20 @@ describe DynamoDB do
31
31
  DynamoDB.serialize("foo" => nil).should == {}
32
32
  DynamoDB.serialize("foo" => "").should == {}
33
33
  end
34
+
35
+ it "serializes StringSet" do
36
+ DynamoDB.serialize(["foo", "bar", "foo"]).should == {"SS" => ["foo", "bar"]}
37
+ end
38
+
39
+ it "serializes NumberSet" do
40
+ DynamoDB.serialize([1, 2, 1]).should == {"NS" => [1,2]}
41
+ end
42
+
43
+ it "raises an error on mixed types" do
44
+ lambda {
45
+ DynamoDB.serialize([1, "2", 1])
46
+ }.should raise_error
47
+ end
34
48
  end
35
49
 
36
50
  describe "#deserialize" do
@@ -55,5 +69,15 @@ describe DynamoDB do
55
69
  deserialized["price"].should == 11.99
56
70
  deserialized["active"].should == 1
57
71
  end
72
+
73
+ it "deserializes StringSet and NumberSet" do
74
+ item = {
75
+ "turtles" => {"SS" => ["Leonardo", "Michelangelo"]},
76
+ "powerball" => {"NS" => [1,2]}
77
+ }
78
+ deserialized = DynamoDB.deserialize(item)
79
+ deserialized["turtles"].should == ["Leonardo", "Michelangelo"]
80
+ deserialized["powerball"].should == [1,2]
81
+ end
58
82
  end
59
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamodb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,24 +10,8 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-11-15 00:00:00.000000000 Z
13
+ date: 2013-05-08 00:00:00.000000000 Z
14
14
  dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: typhoeus
17
- requirement: !ruby/object:Gem::Requirement
18
- none: false
19
- requirements:
20
- - - '='
21
- - !ruby/object:Gem::Version
22
- version: 0.4.2
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- none: false
27
- requirements:
28
- - - '='
29
- - !ruby/object:Gem::Version
30
- version: 0.4.2
31
15
  - !ruby/object:Gem::Dependency
32
16
  name: multi_json
33
17
  requirement: !ruby/object:Gem::Requirement
@@ -93,14 +77,19 @@ files:
93
77
  - dynamodb.gemspec
94
78
  - lib/dynamodb.rb
95
79
  - lib/dynamodb/connection.rb
96
- - lib/dynamodb/credentials.rb
97
- - lib/dynamodb/response.rb
98
- - lib/dynamodb/security_token_service.rb
99
- - lib/dynamodb/typhoeus/request.rb
80
+ - lib/dynamodb/failure_response.rb
81
+ - lib/dynamodb/http_handler.rb
82
+ - lib/dynamodb/request.rb
83
+ - lib/dynamodb/success_response.rb
84
+ - lib/dynamodb/version.rb
85
+ - lib/net/http/connection_pool.rb
86
+ - lib/net/http/connection_pool/connection.rb
87
+ - lib/net/http/connection_pool/session.rb
100
88
  - spec/dynamodb/connection_spec.rb
101
- - spec/dynamodb/credentials_spec.rb
102
- - spec/dynamodb/response_spec.rb
103
- - spec/dynamodb/security_token_service_spec.rb
89
+ - spec/dynamodb/failure_response_spec.rb
90
+ - spec/dynamodb/http_handler_spec.rb
91
+ - spec/dynamodb/request_spec.rb
92
+ - spec/dynamodb/success_response_spec.rb
104
93
  - spec/dynamodb_spec.rb
105
94
  - spec/spec_helper.rb
106
95
  homepage: http://github.com/groupme/dynamodb
@@ -129,8 +118,9 @@ specification_version: 3
129
118
  summary: Communicate with Amazon DynamoDB.
130
119
  test_files:
131
120
  - spec/dynamodb/connection_spec.rb
132
- - spec/dynamodb/credentials_spec.rb
133
- - spec/dynamodb/response_spec.rb
134
- - spec/dynamodb/security_token_service_spec.rb
121
+ - spec/dynamodb/failure_response_spec.rb
122
+ - spec/dynamodb/http_handler_spec.rb
123
+ - spec/dynamodb/request_spec.rb
124
+ - spec/dynamodb/success_response_spec.rb
135
125
  - spec/dynamodb_spec.rb
136
126
  - spec/spec_helper.rb
@@ -1,30 +0,0 @@
1
- module DynamoDB
2
- class Credentials
3
- attr_reader :access_key_id, :secret_access_key, :session_token
4
-
5
- def self.from_hash(hash)
6
- new(hash["access_key_id"], hash["secret_access_key"], hash["session_token"])
7
- end
8
-
9
- def initialize(access_key_id, secret_access_key, session_token)
10
- @access_key_id = access_key_id
11
- @secret_access_key = secret_access_key
12
- @session_token = session_token
13
- end
14
-
15
- def to_hash
16
- {
17
- "access_key_id" => access_key_id,
18
- "secret_access_key" => secret_access_key,
19
- "session_token" => session_token
20
- }
21
- end
22
-
23
- def ==(other)
24
- self.class == other.class &&
25
- access_key_id == other.access_key_id &&
26
- secret_access_key == other.secret_access_key &&
27
- session_token == other.session_token
28
- end
29
- end
30
- end
@@ -1,33 +0,0 @@
1
- module DynamoDB
2
- class Response
3
- attr_reader :typhoeus_response
4
-
5
- def initialize(typhoeus_response)
6
- @typhoeus_response = typhoeus_response
7
- end
8
-
9
- def hash_key_element
10
- DynamoDB.deserialize(json["LastEvaluatedKey"]["HashKeyElement"])
11
- end
12
-
13
- def range_key_element
14
- DynamoDB.deserialize(json["LastEvaluatedKey"]["RangeKeyElement"])
15
- end
16
-
17
- def item
18
- return unless json["Item"]
19
- @item ||= DynamoDB.deserialize(json["Item"])
20
- end
21
-
22
- def items
23
- return unless json["Items"]
24
- @items ||= json["Items"].map { |i| DynamoDB.deserialize(i) }
25
- end
26
-
27
- private
28
-
29
- def json
30
- @json ||= MultiJson.load(typhoeus_response.body)
31
- end
32
- end
33
- end
@@ -1,110 +0,0 @@
1
- module DynamoDB
2
- attr_writer :access_key_id
3
- attr_writer :secret_acces_key
4
- attr_writer :session_token
5
-
6
- # SecurityTokenService automatically manages the creation and renewal of
7
- # temporary AWS credentials.
8
- #
9
- # Usage:
10
- #
11
- # credentials = SecurityTokenService.new "id", "secret key"
12
- # credentials.access_key_id # => String
13
- # credentials.secret_access_key # => String
14
- # credentials.session_token # => String
15
- #
16
- class SecurityTokenService
17
- THIRTY_SIX_HOURS = 129600
18
-
19
- # A SecurityTokenService is initialized for a single AWS user using his
20
- # credentials.
21
- def initialize(access_key_id, secret_access_key)
22
- @_access_key_id = access_key_id
23
- @_secret_access_key = secret_access_key
24
- @credentials = nil
25
- end
26
-
27
- def credentials
28
- obtain_credentials
29
- @credentials
30
- end
31
-
32
- private
33
-
34
- def signature(authorization_params)
35
- sign(string_to_sign(authorization_params))
36
- end
37
-
38
- # The last line needs to be a query string of all parameters
39
- # in the request in alphabetical order.
40
- def string_to_sign(authorization_params)
41
- [
42
- "GET",
43
- "sts.amazonaws.com",
44
- "/",
45
- "AWSAccessKeyId=#{@_access_key_id}" +
46
- "&Action=GetSessionToken" +
47
- "&DurationSeconds=#{THIRTY_SIX_HOURS}" +
48
- "&SignatureMethod=HmacSHA256" +
49
- "&SignatureVersion=2" +
50
- "&Timestamp=#{CGI.escape(authorization_params[:Timestamp])}" +
51
- "&Version=2011-06-15"
52
- ].join("\n")
53
- end
54
-
55
- # Extract the contents of a given tag.
56
- def get_tag(tag, string)
57
- # Considering that the XML string received from STS is sane and always
58
- # has the same simple structure, I think a simple regular expression
59
- # can do the job (with the benefit of not adding a dependency on
60
- # another library just for ONE method). I will switch to Nokogiri if
61
- # needed.
62
- string.match(/#{tag.to_s}>([^<]*)/)[1]
63
- end
64
-
65
- # Obtain temporary credentials, set to expire after 1 hour. If
66
- # credentials were previously obtained, no request is made until they
67
- # expire.
68
- def obtain_credentials
69
- return unless credentials_expired?
70
-
71
- authorization_params = {
72
- :Action => 'GetSessionToken',
73
- :Timestamp => Time.now.utc.iso8601,
74
- :Version => '2011-06-15',
75
- :DurationSeconds => THIRTY_SIX_HOURS # 36 hour expiration
76
- }
77
-
78
- params = {
79
- :AWSAccessKeyId => @_access_key_id,
80
- :SignatureMethod => 'HmacSHA256',
81
- :SignatureVersion => '2',
82
- :Signature => signature(authorization_params)
83
- }.merge(authorization_params)
84
-
85
- response = Typhoeus::Request.get("https://sts.amazonaws.com", :params => params)
86
- if response.success?
87
- body = response.body
88
- @expiration = Time.parse(get_tag(:Expiration, body))
89
- @credentials = Credentials.new(
90
- get_tag(:AccessKeyId, body),
91
- get_tag(:SecretAccessKey, body),
92
- get_tag(:SessionToken, body))
93
- else
94
- raise AuthenticationError.new(response)
95
- end
96
- end
97
-
98
- # Sign (HMAC-SHA256) a string using the secret key given at
99
- # initialization.
100
- def sign(string)
101
- Base64.encode64(
102
- OpenSSL::HMAC.digest('sha256', @_secret_access_key, string)
103
- ).strip
104
- end
105
-
106
- def credentials_expired?
107
- @expiration.nil? || @expiration <= Time.now.utc
108
- end
109
- end
110
- end
@@ -1,27 +0,0 @@
1
- module Typhoeus
2
- class Request
3
- def sign(credentials)
4
- headers.merge!('x-amzn-authorization' => "AWS3 AWSAccessKeyId=#{credentials.access_key_id},Algorithm=HmacSHA256,Signature=#{digest(credentials.secret_access_key)}")
5
- end
6
-
7
- private
8
-
9
- def digest(secret_key)
10
- Base64.encode64(
11
- OpenSSL::HMAC.digest('sha256', secret_key, Digest::SHA256.digest(string_to_sign))
12
- ).strip
13
- end
14
-
15
- def string_to_sign
16
- "POST\n/\n\nhost:#{parsed_uri.host}\n#{amz_to_sts}\n#{body}"
17
- end
18
-
19
- def amz_to_sts
20
- get_amz_headers.sort.map {|key, val| [key, val].join(':') + "\n"}.join
21
- end
22
-
23
- def get_amz_headers
24
- headers.select {|key, val| key =~ /\Ax-amz-/}
25
- end
26
- end
27
- end
@@ -1,22 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe DynamoDB::Credentials do
4
- describe "#==" do
5
- it "is true for identical credentials" do
6
- DynamoDB::Credentials.new("abc", "123", "token").should ==
7
- DynamoDB::Credentials.new("abc", "123", "token")
8
- end
9
-
10
- it "is false otherwise" do
11
- DynamoDB::Credentials.new("abc", "123", "token").should_not ==
12
- DynamoDB::Credentials.new("abc", "123", "different token")
13
- end
14
- end
15
-
16
- describe "#to_hash" do
17
- it "converts to a hash and back" do
18
- credentials = DynamoDB::Credentials.new("abc", "123", "token")
19
- DynamoDB::Credentials.from_hash(credentials.to_hash).should == credentials
20
- end
21
- end
22
- end
@@ -1,87 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe DynamoDB::Response do
4
- before do
5
- body = {
6
- "LastEvaluatedKey" => {
7
- "HashKeyElement" => {"N"=>"1"},
8
- "RangeKeyElement"=>{"N"=>"1501"}
9
- },
10
- "Items"=>[
11
- {
12
- "name"=>{"S"=>"John Smith"},
13
- "created_at"=>{"N"=>"1321564309.99428"},
14
- "disabled"=>{"N"=>"0"},
15
- "group_id"=>{"N"=>"1"},
16
- "person_id"=>{"N" => "1500"}
17
- },
18
- {
19
- "name"=>{"S"=>"Jane Smith"},
20
- "created_at"=>{"N"=>"1321564309.99428"},
21
- "disabled"=>{"N"=>"1"},
22
- "group_id"=>{"N"=>"1"},
23
- "person_id"=>{"N" => "1501"}
24
- }
25
- ],
26
- "Count" => 1,
27
- "ConsumedCapacityUnits" => 0.5
28
- }
29
- @typhoeus_response = mock("response", :body => MultiJson.dump(body))
30
- end
31
-
32
- describe "#hash_key_element" do
33
- it "returns the typecast value of HashKeyElement" do
34
- response = DynamoDB::Response.new(@typhoeus_response)
35
- response.hash_key_element.should == 1
36
- end
37
- end
38
-
39
- describe "#range_key_element" do
40
- it "returns the typecast value of RangeKeyElement" do
41
- response = DynamoDB::Response.new(@typhoeus_response)
42
- response.range_key_element.should == 1501
43
- end
44
- end
45
-
46
- context "Items" do
47
- it "type casts response" do
48
- response = DynamoDB::Response.new(@typhoeus_response)
49
- response.items[0]["name"].should == "John Smith"
50
- response.items[0]["created_at"].should == 1321564309.99428
51
- response.items[0]["disabled"].should == 0
52
- response.items[0]["group_id"].should == 1
53
- response.items[0]["person_id"].should == 1500
54
-
55
- response.items[1]["name"].should == "Jane Smith"
56
- response.items[1]["created_at"].should == 1321564309.99428
57
- response.items[1]["disabled"].should == 1
58
- response.items[1]["group_id"].should == 1
59
- response.items[1]["person_id"].should == 1501
60
- end
61
- end
62
-
63
- context "Item" do
64
- before do
65
- body = {
66
- "Item"=> {
67
- "name"=>{"S"=>"John Smith"},
68
- "created_at"=>{"N"=>"1321564309.99428"},
69
- "disabled"=>{"N"=>"0"},
70
- "group_id"=>{"N"=>"1"},
71
- "person_id"=>{"N" => "1500"}
72
- },
73
- "ConsumedCapacityUnits" => 0.5
74
- }
75
- @typhoeus_response = mock("response", :body => MultiJson.dump(body))
76
- end
77
-
78
- it "type casts response" do
79
- response = DynamoDB::Response.new(@typhoeus_response)
80
- response.item["name"].should == "John Smith"
81
- response.item["created_at"].should == 1321564309.99428
82
- response.item["disabled"].should == 0
83
- response.item["group_id"].should == 1
84
- response.item["person_id"].should == 1500
85
- end
86
- end
87
- end
@@ -1,97 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- module DynamoDB
4
- describe SecurityTokenService do
5
- let(:sts) { SecurityTokenService.new("access_key_id", "secret_access_key") }
6
-
7
- context "success" do
8
- before do
9
- Time.stub(:now).and_return(Time.parse("2012-03-24T22:10:38Z"))
10
- success_body = <<-XML
11
- <GetSessionTokenResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
12
- <GetSessionTokenResult>
13
- <Credentials>
14
- <SessionToken>session_token</SessionToken>
15
- <SecretAccessKey>secret_access_key</SecretAccessKey>
16
- <Expiration>2036-03-19T01:03:22.276Z</Expiration>
17
- <AccessKeyId>access_key_id</AccessKeyId>
18
- </Credentials>
19
- </GetSessionTokenResult>
20
- <ResponseMetadata>
21
- <RequestId>f0fa5827-7156-11e1-8f1e-a92b58fdc66e</RequestId>
22
- </ResponseMetadata>
23
- </GetSessionTokenResponse>
24
- XML
25
-
26
- @request = stub_request(:get, "https://sts.amazonaws.com/").
27
- with(:query => {
28
- "AWSAccessKeyId" => "access_key_id",
29
- "Action" => "GetSessionToken",
30
- "Signature" => "HyF65paDxprCe+zEHx7wxah+hiZ43PhvtmeehDtfuw8=",
31
- "SignatureMethod" => "HmacSHA256",
32
- "SignatureVersion" => "2",
33
- "Timestamp" => "2012-03-24T22:10:38Z",
34
- "Version" => "2011-06-15",
35
- "DurationSeconds" => "129600"
36
- }).to_return(:status => 200, :body => success_body)
37
- end
38
-
39
- it "obtains session_token, access_key_id, secret_access_key" do
40
- sts = SecurityTokenService.new("access_key_id", "secret_access_key")
41
- credentials = sts.credentials
42
- credentials.access_key_id.should == "access_key_id"
43
- credentials.secret_access_key.should == "secret_access_key"
44
- credentials.session_token.should == "session_token"
45
- end
46
-
47
- it "does not query STS if the credentials are not expired" do
48
- sts = SecurityTokenService.new("access_key_id", "secret_access_key")
49
-
50
- sts.stub(:credentials_expired?).and_return(true)
51
- sts.credentials
52
- @request.should have_been_requested
53
-
54
- WebMock.reset!
55
-
56
- sts.stub(:credentials_expired?).and_return(false)
57
- sts.credentials
58
- @request.should_not have_been_requested
59
- end
60
- end
61
-
62
- context "failure" do
63
- before do
64
- Time.stub(:now).and_return(Time.parse("2012-03-24T22:10:38Z"))
65
- error_body = <<-XML
66
- <ErrorResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
67
- <Error>
68
- <Type>Sender</Type>
69
- <Code>InvalidClientTokenId</Code>
70
- <Message>The security token included in the request is invalid</Message>
71
- </Error>
72
- <RequestId>a9f51cd0-7f5b-11e1-8022-bd0f2fc51c4b</RequestId>
73
- </ErrorResponse>
74
- XML
75
-
76
- stub_request(:get, "https://sts.amazonaws.com/").
77
- with(:query => {
78
- "AWSAccessKeyId" => "access_key_id",
79
- "Action" => "GetSessionToken",
80
- "Signature" => "HyF65paDxprCe+zEHx7wxah+hiZ43PhvtmeehDtfuw8=",
81
- "SignatureMethod" => "HmacSHA256",
82
- "SignatureVersion" => "2",
83
- "Timestamp" => "2012-03-24T22:10:38Z",
84
- "Version" => "2011-06-15",
85
- "DurationSeconds" => "129600"
86
- }).to_return(:status => 403, :body => error_body)
87
- end
88
-
89
- it "raises an AuthenticationError" do
90
- s = SecurityTokenService.new("access_key_id", "secret_access_key")
91
- proc {
92
- s.credentials
93
- }.should raise_error(DynamoDB::AuthenticationError)
94
- end
95
- end
96
- end
97
- end