fake_dynamo 0.0.5 → 0.0.6

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