dynamodb 0.0.2 → 1.1.1

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.
@@ -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