aws-sdk 1.2.6 → 1.3.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 (60) hide show
  1. data/lib/aws.rb +2 -0
  2. data/lib/aws/api_config/DynamoDB-2011-12-05.yml +721 -0
  3. data/lib/aws/core.rb +10 -1
  4. data/lib/aws/core/client.rb +17 -12
  5. data/lib/aws/core/configuration.rb +13 -3
  6. data/lib/aws/core/configured_json_client_methods.rb +71 -0
  7. data/lib/aws/core/lazy_error_classes.rb +7 -2
  8. data/lib/aws/core/option_grammar.rb +67 -13
  9. data/lib/aws/core/resource.rb +9 -1
  10. data/lib/aws/core/session_signer.rb +95 -0
  11. data/lib/aws/dynamo_db.rb +169 -0
  12. data/lib/aws/dynamo_db/attribute_collection.rb +460 -0
  13. data/lib/aws/dynamo_db/batch_get.rb +206 -0
  14. data/lib/aws/dynamo_db/client.rb +119 -0
  15. data/lib/aws/dynamo_db/config.rb +20 -0
  16. data/lib/aws/dynamo_db/errors.rb +57 -0
  17. data/lib/aws/dynamo_db/expectations.rb +40 -0
  18. data/lib/aws/dynamo_db/item.rb +130 -0
  19. data/lib/aws/dynamo_db/item_collection.rb +837 -0
  20. data/lib/aws/{record/optimistic_locking.rb → dynamo_db/item_data.rb} +9 -12
  21. data/lib/aws/{record/attributes/boolean.rb → dynamo_db/keys.rb} +15 -23
  22. data/lib/aws/dynamo_db/primary_key_element.rb +47 -0
  23. data/lib/aws/dynamo_db/request.rb +78 -0
  24. data/lib/aws/{record/attributes/float.rb → dynamo_db/resource.rb} +10 -25
  25. data/lib/aws/dynamo_db/table.rb +418 -0
  26. data/lib/aws/dynamo_db/table_collection.rb +165 -0
  27. data/lib/aws/dynamo_db/types.rb +86 -0
  28. data/lib/aws/ec2/resource_tag_collection.rb +3 -1
  29. data/lib/aws/record.rb +36 -8
  30. data/lib/aws/record/abstract_base.rb +642 -0
  31. data/lib/aws/record/attributes.rb +384 -0
  32. data/lib/aws/record/dirty_tracking.rb +0 -1
  33. data/lib/aws/record/errors.rb +0 -8
  34. data/lib/aws/record/hash_model.rb +163 -0
  35. data/lib/aws/record/hash_model/attributes.rb +182 -0
  36. data/lib/aws/record/hash_model/finder_methods.rb +178 -0
  37. data/lib/aws/record/hash_model/scope.rb +108 -0
  38. data/lib/aws/record/model.rb +429 -0
  39. data/lib/aws/record/model/attributes.rb +377 -0
  40. data/lib/aws/record/model/finder_methods.rb +232 -0
  41. data/lib/aws/record/model/scope.rb +213 -0
  42. data/lib/aws/record/scope.rb +43 -169
  43. data/lib/aws/record/validations.rb +11 -11
  44. data/lib/aws/s3/client.rb +9 -6
  45. data/lib/aws/s3/object_collection.rb +1 -1
  46. data/lib/aws/simple_db/expect_condition_option.rb +1 -1
  47. data/lib/aws/simple_db/item_collection.rb +5 -3
  48. data/lib/aws/sts/client.rb +9 -0
  49. metadata +73 -30
  50. data/lib/aws/record/attribute.rb +0 -94
  51. data/lib/aws/record/attribute_macros.rb +0 -312
  52. data/lib/aws/record/attributes/date.rb +0 -89
  53. data/lib/aws/record/attributes/datetime.rb +0 -86
  54. data/lib/aws/record/attributes/integer.rb +0 -68
  55. data/lib/aws/record/attributes/sortable_float.rb +0 -60
  56. data/lib/aws/record/attributes/sortable_integer.rb +0 -95
  57. data/lib/aws/record/attributes/string.rb +0 -69
  58. data/lib/aws/record/base.rb +0 -828
  59. data/lib/aws/record/finder_methods.rb +0 -230
  60. data/lib/aws/record/scopes.rb +0 -55
@@ -0,0 +1,206 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class DynamoDB
16
+
17
+ # A utility class for configuring a list of tables, attributes and
18
+ # items to request information for.
19
+ #
20
+ # @see DynamoDB#batch_get
21
+ # @see Table#batch_get
22
+ #
23
+ class BatchGet
24
+
25
+ include Keys
26
+ include Enumerable
27
+ include Core::Model
28
+
29
+ def initialize options = {}
30
+ super(options)
31
+ @request_items = {}
32
+ end
33
+
34
+ # Add a list of items to fetch in this batch.
35
+ #
36
+ # @param [String] table_name The name of the table to fetch attributes
37
+ # from.
38
+ #
39
+ # @param [Symbol, String, Array<String>] attributes The list of attributes
40
+ # to fetch. If you want to load *ALL* attributes for the named items,
41
+ # then pass the symbol +:all+.
42
+ #
43
+ # # get all attributes
44
+ # batch_get.table('mytable', :all, items)
45
+ #
46
+ # # get one attribute for each item
47
+ # batch_get.table('mytable', ['name'], items)
48
+ #
49
+ # # get a list of attributes for each item
50
+ # batch_get.table('mytable', ['name', 'size'], items)
51
+ #
52
+ # @param [Array<Item,Array>] items One or more items to fetch attributes
53
+ # for. Each attribute should be one of the following:
54
+ #
55
+ # * an {Item} object
56
+ # * a hash key value
57
+ # * a hash key value and a range key value
58
+ #
59
+ # You must provide both the hash key and range key values if the table
60
+ # schema has both.
61
+ #
62
+ # batch_get.table('mytable', :all, [%w(hk1 rk1), %w(hk1 rk2), ...])
63
+ #
64
+ # @return [nil]
65
+ #
66
+ def table table, attributes, items
67
+
68
+ table = table.is_a?(Table) ? table.name : table.to_s
69
+
70
+ attributes = attributes == :all ? nil : [attributes].flatten
71
+
72
+ keys = items.collect do |item|
73
+ case item
74
+ when Item then item_key_hash(item)
75
+ when Array
76
+ {
77
+ :hash_key_element => format_attribute_value(item[0]),
78
+ :range_key_element => format_attribute_value(item[1]),
79
+ }
80
+ else
81
+ { :hash_key_element => format_attribute_value(item) }
82
+ end
83
+ end
84
+
85
+ ## ensure we don't receive 2 different lists of attributes for
86
+ ## the same table
87
+
88
+ if
89
+ @request_items.has_key?(table) and
90
+ @request_items[table][:attributes_to_get] != attributes
91
+ then
92
+ msg = "When batch getting attributes, you may only provide " +
93
+ "1 list of attributes per table, but the `#{table}` table " +
94
+ "has received reqeusts for 2 different sets of attributes"
95
+ raise ArgumentError, msg
96
+ end
97
+
98
+ ## merge attributes and items with the request items
99
+
100
+ @request_items[table] ||= { :keys => [] }
101
+ @request_items[table][:attributes_to_get] = attributes if attributes
102
+ @request_items[table][:keys] += keys
103
+
104
+ nil
105
+
106
+ end
107
+
108
+ # Specify a list of {Item} objects to batch fetch attributes for.
109
+ # The table name is retrieved from the items objects, this means
110
+ # the items do not need to belong to the same table.
111
+ #
112
+ # @param [Symbol, String, Array<String>] attributes The list of attributes
113
+ # to fetch. If you want to load *ALL* attributes for the named items,
114
+ # then pass the symbol +:all+.
115
+ #
116
+ # # get all attributes
117
+ # batch_get.table('mytable', :all, items)
118
+ #
119
+ # # get one attribute for each item
120
+ # batch_get.table('mytable', ['name'], items)
121
+ #
122
+ # # get a list of attributes for each item
123
+ # batch_get.table('mytable', ['name', 'size'], items)
124
+ #
125
+ # @param [Item] items One or more {Item} objects to fetch attributes
126
+ # for. These items may come from any number of different tables.
127
+ #
128
+ def items attributes, *items
129
+ [items].flatten.each do |item|
130
+ self.table(item.table, attributes, [item])
131
+ end
132
+ end
133
+
134
+ # @return [nil]
135
+ def each &block
136
+
137
+ options = { :request_items => @request_items }
138
+
139
+ begin
140
+
141
+ response = client.batch_get_item(options)
142
+
143
+ response.data['Responses'].each_pair do |table_name,details|
144
+ details['Items'].each do |hash_data|
145
+ attributes = values_from_response_hash(hash_data)
146
+ yield(table_name, attributes)
147
+ end
148
+ end
149
+
150
+ options[:request_items] = convert_unprocessed_keys(response)
151
+
152
+ end while options[:request_items]
153
+
154
+ nil
155
+
156
+ end
157
+
158
+ # Yields only attribute hashes. This removes the outer hash that
159
+ # normally provides the :table_name and :attributes keys. This is
160
+ # useful when your batch get requested items from a single table.
161
+ def each_attributes
162
+ each do |table_name, attributes|
163
+ yield(attributes)
164
+ end
165
+ end
166
+
167
+ protected
168
+ def convert_unprocessed_keys response
169
+
170
+ return nil if response.data['UnprocessedKeys'].empty?
171
+
172
+ # convert the json response keys into symbolized keys
173
+ str2sym = lambda do |key_desc|
174
+ type, value = key_desc.to_a.flatten
175
+ case type
176
+ when "S" then { :s => value }
177
+ when "N" then { :n => value }
178
+ else
179
+ raise "unhandled key type: #{type}"
180
+ end
181
+ end
182
+
183
+ request_items = {}
184
+ response.data['UnprocessedKeys'].each_pair do |table,keys|
185
+
186
+ request_items[table] = {}
187
+
188
+ request_items[table][:attributes_to_get] = keys['AttributesToGet'] if
189
+ keys['AttributesToGet']
190
+
191
+ request_items[table][:keys] = keys['Keys'].collect do |desc|
192
+ key = {}
193
+ key[:hash_key_element] = str2sym.call(desc['HashKeyElement'])
194
+ key[:range_key_element] = str2sym.call(desc['RangeKeyElement']) if
195
+ desc['RangeKeyElement']
196
+ key
197
+ end
198
+
199
+ end
200
+ request_items
201
+
202
+ end
203
+
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,119 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class DynamoDB
16
+
17
+ # @private
18
+ class Client < Core::Client
19
+
20
+ API_VERSION = '2011-06-01'
21
+
22
+ REGION_US_E1 = 'dynamodb.us-east-1.amazonaws.com'
23
+
24
+ TARGET_PREFIX = "DynamoDB_20111205."
25
+
26
+ REQUEST_CLASS = DynamoDB::Request
27
+
28
+ CACHEABLE_REQUESTS = Set[:list_tables, :describe_table]
29
+
30
+ include Core::ConfiguredJsonClientMethods
31
+
32
+ configure_client
33
+
34
+ def initialize *args
35
+
36
+ super
37
+
38
+ # If the signer does not provide a session token, then we will
39
+ # replace it with another signer that manages an AWS STS session.
40
+ # This session will auto renew whenever it has expired and will
41
+ # be shared across threads.
42
+ if config.signer.session_token.nil?
43
+ @signer = Core::SessionSigner.for(config)
44
+ end
45
+
46
+ end
47
+
48
+ protected
49
+ def new_request
50
+ req = super
51
+ req.headers["content-type"] = "application/x-amz-json-1.0"
52
+ req
53
+ end
54
+
55
+ protected
56
+ def extract_error_code response
57
+ if response.http_response.status == 413
58
+ 'RequestEntityTooLarge'
59
+ elsif response.http_response.status >= 300 and
60
+ body = response.http_response.body and
61
+ json = (JSON.load(body) rescue nil) and
62
+ type = json["__type"] and
63
+ type =~ /\#(.*)$/
64
+ $1
65
+ end
66
+ end
67
+
68
+ private
69
+ def should_retry? response
70
+ if possible_expired_credentials?(response)
71
+ true
72
+ elsif response.error.is_a?(Errors::ProvisionedThroughputExceededException)
73
+ config.dynamo_db_retry_throughput_errors?
74
+ else
75
+ super
76
+ end
77
+ end
78
+
79
+ private
80
+ def rebuild_http_request response
81
+ # called when a request is going to be retried, in case of
82
+ # expired credentials we should refresh the session
83
+ signer.refresh_session if possible_expired_credentials?(response)
84
+ super
85
+ end
86
+
87
+ private
88
+ def sleep_durations response
89
+
90
+ retry_count =
91
+ if possible_expired_credentials?(response)
92
+ config.max_retries == 0 ? 0 : 1
93
+ else
94
+ config.max_retries { 10 }
95
+ end
96
+
97
+ # given a retry_count of 10, the sleep durations will look like:
98
+ # 0, 50, 100, 200, 400, 800, 1600, 3200, 6400, 12800 (milliseconds)
99
+ (0...retry_count).map do |n|
100
+ if n == 0
101
+ 0
102
+ else
103
+ 50 * (2 ** (n - 1)) / 1000.0
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ private
110
+ # Returns true if we get an access denied error from the service AND
111
+ # our signer is capible of getting new short-term credentials
112
+ def possible_expired_credentials? response
113
+ signer.respond_to?(:refresh_session) and
114
+ response.error.is_a?(Errors::AccessDeniedException)
115
+ end
116
+
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,20 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ AWS::Core::Configuration.module_eval do
15
+
16
+ add_service 'DynamoDB', 'dynamo_db', 'dynamodb.us-east-1.amazonaws.com'
17
+
18
+ add_option :dynamo_db_retry_throughput_errors, true, :boolean => true
19
+
20
+ end
@@ -0,0 +1,57 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class DynamoDB
16
+
17
+ module Errors
18
+
19
+ module ModeledError
20
+
21
+ def initialize(request = nil, response = nil)
22
+ message = extract_message(response)
23
+ include_error_type(response) if response
24
+ super(request, response, message)
25
+ end
26
+
27
+ def extract_message(response)
28
+ if response and response.body != ''
29
+ JSON.load(response.body)["message"] || code
30
+ elsif code == 'RequestEntityTooLarge'
31
+ 'Request body must be less than 1 MB'
32
+ else
33
+ code
34
+ end
35
+ end
36
+
37
+ def include_error_type(response)
38
+ if response.status >= 500
39
+ extend Errors::ServerError
40
+ else
41
+ extend Errors::ClientError
42
+ end
43
+ end
44
+
45
+ def code
46
+ self.class.name =~ /(::)?([^:]+)$/
47
+ $2
48
+ end
49
+
50
+ end
51
+
52
+ include Core::LazyErrorClasses
53
+
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class DynamoDB
16
+
17
+ module Expectations
18
+
19
+ private
20
+ def expect_conditions(options)
21
+ expected = {}
22
+
23
+ options[:if].each do |name, value|
24
+ context = "expected value for attribute #{name}"
25
+ expected[name.to_s] = {
26
+ :value => format_attribute_value(value, context)
27
+ }
28
+ end if options[:if]
29
+
30
+ [options[:unless_exists]].flatten.each do |name|
31
+ expected[name.to_s] = { :exists => false }
32
+ end if options[:unless_exists]
33
+
34
+ expected
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,130 @@
1
+ # Copyright 2011-2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
4
+ # may not use this file except in compliance with the License. A copy of
5
+ # the License is located at
6
+ #
7
+ # http://aws.amazon.com/apache2.0/
8
+ #
9
+ # or in the "license" file accompanying this file. This file is
10
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ # ANY KIND, either express or implied. See the License for the specific
12
+ # language governing permissions and limitations under the License.
13
+
14
+ module AWS
15
+ class DynamoDB
16
+
17
+ # Represents a DynamoDB item. An item is identified by simple or
18
+ # complex primary key (according to the table schema) and consists
19
+ # of a collection of attributes. Attributes are name/value pairs
20
+ # where the value may be a string, number, string set, or number
21
+ # set.
22
+ #
23
+ # Getting an item by hash key value:
24
+ #
25
+ # item = table.items['hash-key-value']
26
+ #
27
+ # Getting an item from a table with both hash and range keys:
28
+ #
29
+ # item = table.items['hash-key','range-key']
30
+ #
31
+ class Item < Core::Resource
32
+
33
+ extend Types
34
+ include Keys
35
+ include Expectations
36
+
37
+ # @return [Table] The table in which the item is stored.
38
+ attr_reader :table
39
+
40
+ # @return [String, Numeric] The hash key value of the item.
41
+ attr_reader :hash_value
42
+
43
+ # @return [String, Numeric, nil] The range key value of the
44
+ # item, or +nil+ if the table has a simple primary key.
45
+ attr_reader :range_value
46
+
47
+ # @private
48
+ def initialize(table, *args)
49
+ opts = args.pop if args.last.kind_of?(Hash)
50
+ (@hash_value, @range_value) = args
51
+ @table = table
52
+ super(table, opts)
53
+ end
54
+
55
+ # Deletes the item.
56
+ #
57
+ # @param [Hash] options Options for deleting the item.
58
+ #
59
+ # @option options [Hash] :if Designates a conditional delete.
60
+ # The operation will fail unless the item exists and has the
61
+ # attributes in the value for this option. For example:
62
+ #
63
+ # # throws DynamoDB::Errors::ConditionalCheckFailedException
64
+ # # unless the item has "color" set to "red"
65
+ # item.delete(:if => { :color => "red" })
66
+ #
67
+ # @option options [String, Symbol, Array] :unless_exists A name
68
+ # or collection of attribute names; if the item has a value
69
+ # for any of these attributes, this method will raise
70
+ # +DynamoDB::Errors::ConditionalCheckFailedException+. For
71
+ # example:
72
+ #
73
+ # item.delete(:unless_exists => "version")
74
+ def delete(options = {})
75
+ client_opts = item_key_options(self)
76
+
77
+ expected = expect_conditions(options)
78
+ client_opts[:expected] = expected unless expected.empty?
79
+
80
+ client_opts[:return_values] = options[:return].to_s.upcase if
81
+ options[:return]
82
+
83
+ resp = client.delete_item(client_opts)
84
+
85
+ values_from_response_hash(resp.data["Attributes"]) if
86
+ options[:return] and resp.data["Attributes"]
87
+ end
88
+
89
+ # @return [Boolean] True if the item exists.
90
+ def exists?(options = {})
91
+ client_opts = item_key_options(self, options)
92
+ client_opts[:attributes_to_get] = [table.hash_key.name]
93
+ resp = client.get_item(client_opts)
94
+ resp.data.key?("Item")
95
+ end
96
+
97
+ # @return [AttributeCollection] An object representing the
98
+ # attributes of the item.
99
+ def attributes
100
+ AttributeCollection.new(self)
101
+ end
102
+
103
+ # @private
104
+ def self.new_from(op, response_object, table, *args)
105
+ table.assert_schema!
106
+ hash_value =
107
+ value_from_response(response_object[table.hash_key.name])
108
+ range_value =
109
+ value_from_response(response_object[table.range_key.name]) if
110
+ table.range_key
111
+
112
+ raise "missing hash key value in put_item response" unless hash_value
113
+ raise "missing range key value in put_item response" unless
114
+ range_value || !table.range_key
115
+
116
+ super(op, response_object,
117
+ table, hash_value, range_value, *args)
118
+ end
119
+
120
+ protected
121
+ def resource_identifiers
122
+ [[:table_name, table.name],
123
+ [:hash_value, hash_value],
124
+ [:range_value, range_value]]
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+ end