fake_dynamo 0.0.5 → 0.0.6

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 CHANGED
@@ -23,7 +23,9 @@ fake_dynamo --port 4567
23
23
  ````ruby
24
24
  # rvmsudo fake_dynamo --port 80
25
25
  AWS.config(:use_ssl => false,
26
- :dynamo_db_endpoint => 'localhost')
26
+ :dynamo_db_endpoint => 'localhost',
27
+ :access_key_id => "xxx",
28
+ :secret_access_key => "xxx")
27
29
  ````
28
30
 
29
31
  # Storage
@@ -557,6 +557,7 @@
557
557
  - :string
558
558
  ComparisonOperator:
559
559
  - :string
560
+ - :required
560
561
  - :enum: [EQ, NE, LE, LT, GE, GT, NOT_NULL, NULL, CONTAINS, NOT_CONTAINS, BEGINS_WITH, IN, BETWEEN]
561
562
  ExclusiveStartKey:
562
563
  - :structure:
@@ -661,6 +662,7 @@
661
662
  - :string
662
663
  ComparisonOperator:
663
664
  - :string
665
+ - :required
664
666
  - :enum: [EQ, LE, LT, GE, GT, BEGINS_WITH, BETWEEN]
665
667
  ScanIndexForward:
666
668
  - :boolean
@@ -720,6 +722,108 @@
720
722
  - :list: member
721
723
  - ConsumedCapacityUnits:
722
724
  - :float
725
+ BatchWriteItem:
726
+ :input:
727
+ RequestItems:
728
+ - :map:
729
+ :key:
730
+ - :string
731
+ :value:
732
+ - :list:
733
+ - :structure:
734
+ PutRequest:
735
+ - :structure:
736
+ Item:
737
+ - :map:
738
+ :key:
739
+ - :string
740
+ :value:
741
+ - :structure:
742
+ S:
743
+ - :string
744
+ N:
745
+ - :string
746
+ SS:
747
+ - :list:
748
+ - :string
749
+ NS:
750
+ - :list:
751
+ - :string
752
+ - :required
753
+ DeleteRequest:
754
+ - :structure:
755
+ Key:
756
+ - :structure:
757
+ HashKeyElement:
758
+ - :structure:
759
+ S:
760
+ - :string
761
+ N:
762
+ - :string
763
+ SS:
764
+ - :list:
765
+ - :string
766
+ NS:
767
+ - :list:
768
+ - :string
769
+ - :required
770
+ RangeKeyElement:
771
+ - :structure:
772
+ S:
773
+ - :string
774
+ N:
775
+ - :string
776
+ SS:
777
+ - :list:
778
+ - :string
779
+ NS:
780
+ - :list:
781
+ - :string
782
+ - :required
783
+ - :required
784
+ :output:
785
+ - Responses:
786
+ - :map:
787
+ - entry
788
+ - key
789
+ - value
790
+ - entry:
791
+ - value:
792
+ - ConsumedCapacityUnits:
793
+ - :float
794
+ - UnprocessedItems:
795
+ - :map:
796
+ - entry
797
+ - key
798
+ - value
799
+ - entry:
800
+ - value:
801
+ - :list: member
802
+ - member:
803
+ - PutRequest:
804
+ - Item:
805
+ - :map:
806
+ - entry
807
+ - key
808
+ - value
809
+ - entry:
810
+ - value:
811
+ - SS:
812
+ - :list: member
813
+ - NS:
814
+ - :list: member
815
+ - DeleteRequest:
816
+ - Key:
817
+ - HashKeyElement:
818
+ - SS:
819
+ - :list: member
820
+ - NS:
821
+ - :list: member
822
+ - RangeKeyElement:
823
+ - SS:
824
+ - :list: member
825
+ - NS:
826
+ - :list: member
723
827
  :client_errors:
724
828
  ConditionalCheckFailed: []
725
829
 
@@ -104,10 +104,68 @@ module FakeDynamo
104
104
  { 'Responses' => response, 'UnprocessedKeys' => {}}
105
105
  end
106
106
 
107
+ def batch_write_item(data)
108
+ response = {}
109
+ items = {}
110
+ request_count = 0
111
+
112
+ # validation
113
+ data['RequestItems'].each do |table_name, requests|
114
+ table = find_table(table_name)
115
+
116
+ items[table.name] ||= {}
117
+
118
+ requests.each do |request|
119
+ if request['PutRequest']
120
+ item = table.batch_put_request(request['PutRequest'])
121
+ check_item_conflict(items, table.name, item.key)
122
+ items[table.name][item.key] = item
123
+ else
124
+ key = table.batch_delete_request(request['DeleteRequest'])
125
+ check_item_conflict(items, table.name, key)
126
+ items[table.name][key] = :delete
127
+ end
128
+
129
+ request_count += 1
130
+ end
131
+ end
132
+
133
+ check_max_request(request_count)
134
+
135
+ # real modification
136
+ items.each do |table_name, requests|
137
+ table = find_table(table_name)
138
+ requests.each do |key, value|
139
+ if value == :delete
140
+ table.batch_delete(key)
141
+ else
142
+ table.batch_put(value)
143
+ end
144
+ end
145
+ response[table_name] = { 'ConsumedCapacityUnits' => 1 }
146
+ end
147
+
148
+ { 'Responses' => response, 'UnprocessedItems' => {} }
149
+ end
150
+
107
151
  private
152
+
153
+ def check_item_conflict(items, table_name, key)
154
+ if items[table_name][key]
155
+ raise ValidationException, 'Provided list of item keys contains duplicates'
156
+ end
157
+ end
158
+
159
+
108
160
  def find_table(table_name)
109
161
  tables[table_name] or raise ResourceNotFoundException, "Table : #{table_name} not found"
110
162
  end
111
163
 
164
+ def check_max_request(count)
165
+ if count > 25
166
+ raise ValidationException, 'Too many items requested for the BatchWriteItem call'
167
+ end
168
+ end
169
+
112
170
  end
113
171
  end
@@ -17,7 +17,7 @@ module FakeDynamo
17
17
  end
18
18
 
19
19
  def write_commands
20
- %w[CreateTable DeleteItem DeleteTable PutItem UpdateItem UpdateTable]
20
+ %w[CreateTable DeleteItem DeleteTable PutItem UpdateItem UpdateTable BatchWriteItem]
21
21
  end
22
22
 
23
23
  def write_command?(command)
@@ -101,6 +101,14 @@ module FakeDynamo
101
101
  consumed_capacity.merge(return_values(data, old_item))
102
102
  end
103
103
 
104
+ def batch_put_request(data)
105
+ Item.from_data(data['Item'], key_schema)
106
+ end
107
+
108
+ def batch_put(item)
109
+ @items[item.key] = item
110
+ end
111
+
104
112
  def get_item(data)
105
113
  response = consumed_capacity
106
114
  if item_hash = get_raw_item(data['Key'], data['AttributesToGet'])
@@ -137,6 +145,14 @@ module FakeDynamo
137
145
  consumed_capacity.merge(return_values(data, item))
138
146
  end
139
147
 
148
+ def batch_delete_request(data)
149
+ Key.from_data(data['Key'], key_schema)
150
+ end
151
+
152
+ def batch_delete(key)
153
+ @items.delete(key)
154
+ end
155
+
140
156
  def update_item(data)
141
157
  key = Key.from_data(data['Key'], key_schema)
142
158
  item = @items[key]
@@ -20,6 +20,7 @@ module FakeDynamo
20
20
 
21
21
  def validate_payload(operation, data)
22
22
  validate! do
23
+ validate_request_size(data)
23
24
  validate_operation(operation)
24
25
  validate_input(operation, data)
25
26
  end
@@ -151,5 +152,11 @@ module FakeDynamo
151
152
  end
152
153
  end
153
154
 
155
+ def validate_request_size(data)
156
+ if data.to_s.bytesize > 1 * 1024 * 1024
157
+ raise ValidationException, "Request size can't exceed 1 mb"
158
+ end
159
+ end
160
+
154
161
  end
155
162
  end
@@ -1,3 +1,3 @@
1
1
  module FakeDynamo
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -253,5 +253,120 @@ module FakeDynamo
253
253
  }.to raise_error(FakeDynamo::ResourceNotFoundException)
254
254
  end
255
255
  end
256
+
257
+ context 'BatchWriteItem' do
258
+ subject do
259
+ db = DB.new
260
+ db.create_table(user_table)
261
+ db
262
+ end
263
+
264
+ it 'should validate payload' do
265
+ expect {
266
+ subject.process('BatchWriteItem', {})
267
+ }.to raise_error(FakeDynamo::ValidationException)
268
+ end
269
+
270
+ it 'should fail if table not found' do
271
+ expect {
272
+ subject.process('BatchWriteItem', {
273
+ 'RequestItems' => {
274
+ 'xxx' => ['DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}]
275
+ }
276
+ })
277
+ }.to raise_error(FakeDynamo::ResourceNotFoundException, /table.*not.*found/i)
278
+ end
279
+
280
+ it 'should fail on conflict items' do
281
+ expect {
282
+ subject.process('BatchWriteItem', {
283
+ 'RequestItems' => {
284
+ 'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}},
285
+ { 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}}]
286
+ }
287
+ })
288
+ }.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
289
+
290
+ expect {
291
+ subject.process('BatchWriteItem', {
292
+ 'RequestItems' => {
293
+ 'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}},
294
+ {'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
295
+ }
296
+ })
297
+ }.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
298
+
299
+ expect {
300
+ subject.process('BatchWriteItem', {
301
+ 'RequestItems' => {
302
+ 'User' => [{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}},
303
+ {'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
304
+ }
305
+ })
306
+ }.to raise_error(FakeDynamo::ValidationException, /duplicate/i)
307
+ end
308
+
309
+ it 'writes/deletes item in the db' do
310
+ response = subject.process('BatchWriteItem', {
311
+ 'RequestItems' => {
312
+ 'User' => [{'PutRequest' => {'Item' => { 'id' => { 'S' => 'ananth'}}}}]
313
+ }
314
+ })
315
+
316
+ response['Responses'].should eq('User' => { 'ConsumedCapacityUnits' => 1 })
317
+
318
+ response = subject.get_item({'TableName' => 'User',
319
+ 'Key' => {'HashKeyElement' => { 'S' => 'ananth'}}})
320
+
321
+ response['Item']['id'].should eq('S' => 'ananth')
322
+
323
+ subject.process('BatchWriteItem', {
324
+ 'RequestItems' => {
325
+ 'User' => [{ 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => 'ananth' }}}}]
326
+ }
327
+ })
328
+
329
+ response = subject.get_item({'TableName' => 'User',
330
+ 'Key' => {'HashKeyElement' => { 'S' => 'ananth'}}})
331
+
332
+ response.should eq({"ConsumedCapacityUnits"=>1})
333
+ end
334
+
335
+ it 'fails it the requested operation is more than 25' do
336
+ expect {
337
+ requests = (1..26).map { |i| { 'DeleteRequest' => { 'Key' => { 'HashKeyElement' => { 'S' => "ananth#{i}" }}}} }
338
+
339
+ subject.process('BatchWriteItem', {
340
+ 'RequestItems' => {
341
+ 'User' => requests
342
+ }
343
+ })
344
+
345
+ }.to raise_error(FakeDynamo::ValidationException, /too many items/i)
346
+ end
347
+
348
+ it 'should fail on request size greater than 1 mb' do
349
+ expect {
350
+
351
+ keys = { 'SS' => (1..2000).map { |i| 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + i.to_s } }
352
+
353
+ requests = (1..25).map do |i|
354
+ {'PutRequest' =>
355
+ {'Item' =>
356
+ { 'id' => { 'S' => 'ananth' + i.to_s },
357
+ 'keys' => keys
358
+ }}}
359
+ end
360
+
361
+
362
+ subject.process('BatchWriteItem', {
363
+ 'RequestItems' => {
364
+ 'User' => requests
365
+ }
366
+ })
367
+
368
+ }.to raise_error(FakeDynamo::ValidationException, /size.*exceed/i)
369
+ end
370
+ end
256
371
  end
257
372
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fake_dynamo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  prerelease:
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-03-28 00:00:00.000000000 Z
12
+ date: 2012-05-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
16
- requirement: &70299635734020 !ruby/object:Gem::Requirement
16
+ requirement: &70284409972980 !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: *70299635734020
24
+ version_requirements: *70284409972980
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70299635733520 !ruby/object:Gem::Requirement
27
+ requirement: &70284409992000 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70299635733520
35
+ version_requirements: *70284409992000
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: json
38
- requirement: &70299635733020 !ruby/object:Gem::Requirement
38
+ requirement: &70284409991540 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70299635733020
46
+ version_requirements: *70284409991540
47
47
  description:
48
48
  email:
49
49
  - ananthakumaran@gmail.com