dynamini 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc4e5ccebe6f491cae52d735aa59d15b307e93ab
4
- data.tar.gz: fc6340c78b0597b67c870e4aa75837c38e2d7c2f
3
+ metadata.gz: 4966ab1242cf3fffce9c1282d02f71c87e9c143f
4
+ data.tar.gz: e9e93317d5d05ae0589ef6f1c01a67325bdc9b4f
5
5
  SHA512:
6
- metadata.gz: f75b4504629a2dbcd718c0fb5c150a7c9911cbd255046fbc180704c13f7350d409beb2567939f4c1ace8d808692ed1ac7d835e615c09f8719f3657a81159ecdc
7
- data.tar.gz: dd4b0336585dfb376f51f4a142a278aaaa8814713aa5c363b50928c70b4ee20ee85d9ec14e6c48ec62581e30789b9e78c54b664b7f4bca1f5b2554943f76fe53
6
+ metadata.gz: 4c971de76f66f13bb5df42b644ba170230d68c07da574d3bed1ecec0a2294984a7fc0eae73bf7d0ea1dead39586a37aaf584df5db3e539503541f7108258a461
7
+ data.tar.gz: a141f1ead3c262ac92274aade2eb0aadf135054bc302ace6a0b7cda74a6f0f229d90917d6148c6fa30447267ee0a1a1f6245be6ae92f87f40f5880b2c9c3f0d3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamini (2.2.0)
4
+ dynamini (2.2.1)
5
5
  activemodel (>= 3, < 5.0)
6
6
  aws-sdk (~> 2)
7
7
 
data/dynamini.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'dynamini'
3
- s.version = '2.2.0'
3
+ s.version = '2.2.1'
4
4
  s.summary = 'DynamoDB interface'
5
5
  s.description = 'Lightweight DynamoDB interface gem designed as
6
6
  a drop-in replacement for ActiveRecord.
@@ -51,10 +51,12 @@ module Dynamini
51
51
  def dynamo_query(args)
52
52
  expression_attribute_values = build_expression_attribute_values(args)
53
53
  key_condition_expression = build_key_condition_expression(args)
54
+ expression_attribute_names = build_expression_attribute_names(args)
54
55
  query = set_extra_parameters(
55
56
  {
56
- table_name: table_name,
57
+ table_name: table_name,
57
58
  key_condition_expression: key_condition_expression,
59
+ expression_attribute_names: expression_attribute_names,
58
60
  expression_attribute_values: expression_attribute_values
59
61
  },
60
62
  args)
@@ -69,14 +71,21 @@ module Dynamini
69
71
  expression_values
70
72
  end
71
73
 
74
+ def build_expression_attribute_names(args)
75
+ expression_values = {}
76
+ expression_values['#H'] = current_index_hash_key(args)
77
+ expression_values['#R'] = current_index_range_key(args) if args[:end] || args[:start]
78
+ expression_values
79
+ end
80
+
72
81
  def build_key_condition_expression(args)
73
- expression = "#{current_index_hash_key(args)} = :h"
82
+ expression = "#H = :h"
74
83
  if args[:start] && args[:end]
75
- expression += " AND #{current_index_range_key(args)} BETWEEN :s AND :e"
84
+ expression += " AND #R BETWEEN :s AND :e"
76
85
  elsif args[:start]
77
- expression += " AND #{current_index_range_key(args)} >= :s"
86
+ expression += " AND #R >= :s"
78
87
  elsif args[:end]
79
- expression += " AND #{current_index_range_key(args)} <= :e"
88
+ expression += " AND #R <= :e"
80
89
  end
81
90
  expression
82
91
  end
@@ -106,15 +106,18 @@ module Dynamini
106
106
  # "foo = val AND bar >= val2"
107
107
  # "foo = val AND bar BETWEEN val2 AND val3"
108
108
 
109
- args[:expression_attribute_values].each do |symbol, value|
110
- args[:key_condition_expression].gsub!(symbol, value.to_s)
111
- end
109
+ attr_placeholders = args[:expression_attribute_values].merge(args[:expression_attribute_names])
110
+ attr_placeholders.each { |symbol, value| args[:key_condition_expression].gsub!(symbol, value.to_s) }
112
111
 
113
112
  tokens = args[:key_condition_expression].split(/\s+/)
113
+
114
+ hash_key_name, range_key_name = determine_hash_and_range(args)
115
+
116
+ inspect_for_correct_keys?(tokens, hash_key_name, range_key_name)
114
117
  start_val, end_val = range_key_limits(tokens)
115
118
 
116
119
  if args[:index_name]
117
- secondary_index_query(args)
120
+ secondary_index_query(args, start_val, end_val)
118
121
  else
119
122
  hash_key = hash_key_value(args).is_a?(Integer) ? tokens[2].to_i : tokens[2]
120
123
 
@@ -126,6 +129,15 @@ module Dynamini
126
129
  end
127
130
  end
128
131
 
132
+ def determine_hash_and_range(args)
133
+ if args[:index_name]
134
+ index = secondary_index[args[:index_name].to_s]
135
+ [index[:hash_key_name].to_s, index[:range_key_name].to_s]
136
+ else
137
+ [@hash_key_attr.to_s, @range_key_attr.to_s]
138
+ end
139
+ end
140
+
129
141
  def range_key_limits(tokens)
130
142
  case tokens[5]
131
143
  when ">=" then [tokens[6], nil]
@@ -144,14 +156,10 @@ module Dynamini
144
156
  records
145
157
  end
146
158
 
147
-
148
- def secondary_index_query(args = {})
149
- index = secondary_index[args[:index_name]]
159
+ def secondary_index_query(args = {}, start_val = nil, end_val = nil)
160
+ index = secondary_index[args[:index_name].to_s]
150
161
  table = get_table(args[:table_name])
151
162
 
152
- tokens = args[:key_condition_expression].split(/\s+/)
153
- start_val, end_val = range_key_limits(tokens)
154
-
155
163
  records = @range_key_attr ? get_values(table) : table.values
156
164
  selected = sort_records(records, index, args, start_val, end_val)
157
165
  OpenStruct.new(items: selected)
@@ -214,5 +222,16 @@ module Dynamini
214
222
 
215
223
  attribute_hash
216
224
  end
225
+
226
+ def inspect_for_correct_keys?(tokens, hash_key_name, range_key_name)
227
+ missed_keys = []
228
+ missed_keys << hash_key_name unless tokens[0] == hash_key_name
229
+ missed_keys << range_key_name unless (tokens.length < 4 || tokens[4] == range_key_name)
230
+ raise missed_key_dynamodb_error(missed_keys) if missed_keys.length > 0
231
+ end
232
+
233
+ def missed_key_dynamodb_error(missed_keys)
234
+ Aws::DynamoDB::Errors::ValidationException.new(400,"Query condition missed key schema element: #{missed_keys.join(', ')}")
235
+ end
217
236
  end
218
237
  end
@@ -105,7 +105,7 @@ describe Dynamini::Querying do
105
105
 
106
106
  context 'hash key does not exist' do
107
107
  it 'should return an empty array' do
108
- expect(TestClassWithRange.query(hash_key: 'non-existent key')).to eq([])
108
+ expect(TestClassWithRange.query(hash_key: 'non-existent-key')).to eq([])
109
109
  end
110
110
  end
111
111
 
@@ -178,9 +178,8 @@ describe Dynamini::Querying do
178
178
  end
179
179
 
180
180
  it 'should return no results if none are found with the secondary index' do
181
- expect(TestClassWithRange.query(hash_key: 'non-existent key', index_name: :secondary_index)).to eq([])
181
+ expect(TestClassWithRange.query(hash_key: 'non-existent-key', index_name: :secondary_index)).to eq([])
182
182
  end
183
-
184
183
  end
185
184
  end
186
185
 
@@ -112,7 +112,8 @@ describe Dynamini::TestClient do
112
112
  test_client.update_item(table_name: 'integer_table', key: {hash_key_field: 1, range_key_field: 1}, attribute_updates: {abc: {value: 'abc', action: 'PUT'}})
113
113
  response = test_client.query(
114
114
  table_name: 'integer_table',
115
- key_condition_expression: "hash_key_field = :h",
115
+ key_condition_expression: "#H = :h",
116
+ expression_attribute_names: {'#H' => 'hash_key_field'},
116
117
  expression_attribute_values: {
117
118
  ":h" => 1
118
119
  }
@@ -123,11 +124,12 @@ describe Dynamini::TestClient do
123
124
  end
124
125
  end
125
126
 
126
- context 'with LE operator' do
127
+ context 'with LE operator' do # broken
127
128
  it 'should return all items with range key less than or equal to the provided value' do
128
129
  response = test_client.query(
129
130
  table_name: table_name,
130
- key_condition_expression: "hash_key_field = :h AND user_id <= :e",
131
+ key_condition_expression: "#H = :h AND #R <= :e",
132
+ expression_attribute_names: {'#H' => 'hash_key_field', '#R' => 'range_key_field'},
131
133
  expression_attribute_values: {
132
134
  ":h" => 'foo',
133
135
  ":e" => 2
@@ -143,7 +145,8 @@ describe Dynamini::TestClient do
143
145
  it 'should return all items with range key greater than or equal to the provided value' do
144
146
  response = test_client.query(
145
147
  table_name: table_name,
146
- key_condition_expression: "hash_key_field = :h AND user_id >= :s",
148
+ key_condition_expression: "#H = :h AND #R >= :s",
149
+ expression_attribute_names: {'#H' => 'hash_key_field', '#R' => 'range_key_field'},
147
150
  expression_attribute_values: {
148
151
  ":h" => 'foo',
149
152
  ":s" => 2
@@ -159,7 +162,8 @@ describe Dynamini::TestClient do
159
162
  it 'should return all items with range key between the provided values' do
160
163
  response = test_client.query(
161
164
  table_name: table_name,
162
- key_condition_expression: "hash_key_field = :h AND user_id BETWEEN :s AND :e",
165
+ key_condition_expression: "#H = :h AND #R BETWEEN :s AND :e",
166
+ expression_attribute_names: {'#H' => 'hash_key_field', '#R' => 'range_key_field'},
163
167
  expression_attribute_values: {
164
168
  ":h" => 'foo',
165
169
  ":s" => 2,
@@ -176,7 +180,8 @@ describe Dynamini::TestClient do
176
180
  it 'should return all items with range key between the provided values' do
177
181
  response = test_client.query(
178
182
  table_name: table_name,
179
- key_condition_expression: "hash_key_field = :h",
183
+ key_condition_expression: "#H = :h",
184
+ expression_attribute_names: {'#H' => 'hash_key_field'},
180
185
  expression_attribute_values: {
181
186
  ":h" => 'foo'
182
187
  }
@@ -187,6 +192,31 @@ describe Dynamini::TestClient do
187
192
  end
188
193
  end
189
194
 
195
+ context 'with invalid expression_attribute_names' do
196
+ it 'should raise an error about an invalid hash_key' do
197
+ expect{ test_client.query(
198
+ table_name: table_name,
199
+ key_condition_expression: "#H = :h",
200
+ expression_attribute_names: {'#H' => 'not_hash_key_field'},
201
+ expression_attribute_values: {
202
+ ':h' => 'foo'
203
+ }
204
+ ) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: hash_key_field")
205
+ end
206
+
207
+ it 'should raise an error about an invalid range_key' do
208
+ expect{ test_client.query(
209
+ table_name: table_name,
210
+ key_condition_expression: "#H = :h AND #R >= :s",
211
+ expression_attribute_names: {'#H' => 'not_hash_key_field', '#R' => 'not_range_key_field'},
212
+ expression_attribute_values: {
213
+ ':h' => 'foo',
214
+ ':s' => 30
215
+ }
216
+ ) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: hash_key_field, range_key_field")
217
+ end
218
+ end
219
+
190
220
  context 'with secondary index' do
191
221
  before do
192
222
  test_client.update_item(table_name: table_name,
@@ -199,7 +229,8 @@ describe Dynamini::TestClient do
199
229
  it 'should return all items with secondary range key less than or equal to the provided value' do
200
230
  response = test_client.query(
201
231
  table_name: table_name,
202
- key_condition_expression: "abc = :h AND secondary_range_key <= :e",
232
+ key_condition_expression: "#H = :h AND #R <= :e",
233
+ expression_attribute_names: {'#H' => 'abc', '#R' => 'secondary_range_key'},
203
234
  expression_attribute_values: {
204
235
  ":h" => 'abc',
205
236
  ":e" => 8
@@ -216,7 +247,8 @@ describe Dynamini::TestClient do
216
247
  it 'should return all items with secondary range key greater than or equal to the provided value' do
217
248
  response = test_client.query(
218
249
  table_name: table_name,
219
- key_condition_expression: "abc = :h AND secondary_range_key >= :s",
250
+ key_condition_expression: "#H = :h AND #R >= :s",
251
+ expression_attribute_names: {'#H' => 'abc', '#R' => 'secondary_range_key'},
220
252
  expression_attribute_values: {
221
253
  ":h" => 'abc',
222
254
  ":s" => 8
@@ -233,7 +265,8 @@ describe Dynamini::TestClient do
233
265
  it 'should return all items with secondary range key between the provided values' do
234
266
  response = test_client.query(
235
267
  table_name: table_name,
236
- key_condition_expression: "abc = :h AND secondary_range_key BETWEEN :s AND :e",
268
+ key_condition_expression: "#H = :h AND #R BETWEEN :s AND :e",
269
+ expression_attribute_names: {'#H' => 'abc', '#R' => 'secondary_range_key'},
237
270
  expression_attribute_values: {
238
271
  ":h" => 'abc',
239
272
  ":s" => 8,
@@ -251,7 +284,8 @@ describe Dynamini::TestClient do
251
284
  it 'should return all items sorted by their secondary index' do
252
285
  response = test_client.query(
253
286
  table_name: table_name,
254
- key_condition_expression: "abc = :h",
287
+ key_condition_expression: "#H = :h",
288
+ expression_attribute_names: {'#H' => 'abc'},
255
289
  expression_attribute_values: {
256
290
  ":h" => 'abc'
257
291
  },
@@ -264,6 +298,32 @@ describe Dynamini::TestClient do
264
298
  end
265
299
  end
266
300
 
301
+ context 'with invalid expression_attribute_names' do
302
+ it 'should raise an error about an invalid hash_key' do
303
+ expect{ test_client.query(
304
+ table_name: table_name,
305
+ key_condition_expression: "#H = :h",
306
+ expression_attribute_names: {'#H' => 'not_hash_key_field'},
307
+ expression_attribute_values: {
308
+ ':h' => 'abc'
309
+ },
310
+ index_name: 'secondary_index'
311
+ ) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: abc")
312
+ end
313
+
314
+ it 'should raise an error about an invalid range_key' do
315
+ expect{ test_client.query(
316
+ table_name: table_name,
317
+ key_condition_expression: "#H = :h AND #R >= :s",
318
+ expression_attribute_names: {'#H' => 'not_hash_key_field', '#R' => 'not_range_key_field'},
319
+ expression_attribute_values: {
320
+ ':h' => 'abc',
321
+ ':s' => 3
322
+ },
323
+ index_name: 'secondary_index'
324
+ ) }.to raise_error(Aws::DynamoDB::Errors::ValidationException, "Query condition missed key schema element: abc, secondary_range_key")
325
+ end
326
+ end
267
327
  end
268
328
 
269
329
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dynamini
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Ward
@@ -15,7 +15,7 @@ authors:
15
15
  autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
- date: 2016-03-08 00:00:00.000000000 Z
18
+ date: 2016-03-09 00:00:00.000000000 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activemodel