dynamodb_framework 1.3.0 → 1.4.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dynamodb_framework/dynamodb_index.rb +119 -0
  3. data/lib/dynamodb_framework/dynamodb_query.rb +111 -0
  4. data/lib/dynamodb_framework/dynamodb_repository.rb +37 -7
  5. data/lib/dynamodb_framework/dynamodb_table.rb +114 -0
  6. data/lib/dynamodb_framework/version.rb +1 -1
  7. data/lib/dynamodb_framework.rb +20 -0
  8. data/spec/dynamodb_index_spec.rb +212 -0
  9. data/spec/dynamodb_migration_manager_spec.rb +134 -0
  10. data/spec/dynamodb_namespace_migration_manager_spec.rb +134 -0
  11. data/spec/dynamodb_query_spec.rb +87 -0
  12. data/spec/dynamodb_repository_spec.rb +306 -0
  13. data/spec/dynamodb_table_manager_spec.rb +156 -0
  14. data/spec/dynamodb_table_spec.rb +245 -0
  15. data/spec/example_index.rb +45 -0
  16. data/spec/example_table.rb +41 -0
  17. data/spec/hash_helper_spec.rb +129 -0
  18. data/spec/spec_helper.rb +34 -0
  19. data/spec/test_item.rb +6 -0
  20. data/spec/test_migration_script1.rb +24 -0
  21. data/spec/test_migration_script2.rb +24 -0
  22. metadata +35 -25
  23. data/.gitignore +0 -14
  24. data/.idea/.name +0 -1
  25. data/.idea/.rakeTasks +0 -7
  26. data/.idea/encodings.xml +0 -6
  27. data/.idea/misc.xml +0 -33
  28. data/.idea/modules.xml +0 -8
  29. data/.idea/vcs.xml +0 -6
  30. data/.rspec +0 -3
  31. data/CODE_OF_CONDUCT.md +0 -49
  32. data/Gemfile +0 -12
  33. data/LICENSE.txt +0 -21
  34. data/README.md +0 -394
  35. data/Rakefile +0 -2
  36. data/dynamodb_framework.gemspec +0 -29
  37. data/script/cleanup.sh +0 -6
  38. data/script/container_loop.sh +0 -6
  39. data/script/docker-compose.yml +0 -5
  40. data/script/restart.sh +0 -3
  41. data/script/start.sh +0 -4
  42. data/script/stop.sh +0 -2
  43. data/yard.sh +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 318aa80e8817e3654c1b398b6ae5610c6306ed1f
4
- data.tar.gz: 23223e4bdf089ff9257a0501c01ae8c1eaa39538
3
+ metadata.gz: 9c4bf2ce79200a61fab5ff066efb0de60535fdd8
4
+ data.tar.gz: 5c338f2559cadf5e0e2d22b83ef2e71afac33b2b
5
5
  SHA512:
6
- metadata.gz: c32e535abdaea1c5361947f781ef81e6db62061840be50f97048a7ea8d4e8ecafe4479a9a43edf04c5eb44847fdddcfdc864332e08f0791bb89be2fdaf7e02d5
7
- data.tar.gz: 154e6bb5af6b03eb69d692a94bd5e67e6e10fa28b8caee2247baaa320e01a8744201e0f5ecba6b4a5c1f949972516faec7815defd50d31cf809af7c70243ff43
6
+ metadata.gz: 5e608a3ad0a435c6daabbbcc07f56e0526f946f31768332e85157fff945911aed2eea695e579a92e19c7036d8b1da0908b6a202a5ec4096c476edde0fc68efa7
7
+ data.tar.gz: ace91a76675791e03f3d5524aaa9718ef4572d460798eb2a452a96b70210cb308e9b39ec44b91f5047003bec6a71304334b75a9c960a97cdb126057a42bc95c2
@@ -0,0 +1,119 @@
1
+ module DynamoDbFramework
2
+ module Index
3
+
4
+ class InvalidConfigException < StandardError
5
+ def initialize(message)
6
+ super(message)
7
+ end
8
+ end
9
+
10
+ def config
11
+ details = {
12
+ index_name: full_index_name,
13
+ table: self.instance_variable_get(:@table),
14
+ partition_key: self.instance_variable_get(:@partition_key)
15
+ }
16
+ if self.instance_variable_defined?(:@range_key)
17
+ details[:range_key] = self.instance_variable_get(:@range_key)
18
+ end
19
+ details
20
+ end
21
+
22
+ def index_name(value)
23
+ self.instance_variable_set(:@index_name, value)
24
+ end
25
+
26
+ def full_index_name
27
+ unless self.instance_variable_defined?(:@index_name)
28
+ raise DynamoDbFramework::Index::InvalidConfigException.new('Index name must be specified.')
29
+ end
30
+ index_name = self.instance_variable_get(:@index_name)
31
+ if DynamoDbFramework.namespace != nil
32
+ index_name = "#{DynamoDbFramework.namespace}#{DynamoDbFramework.namespace_delimiter}#{index_name}"
33
+ end
34
+ index_name
35
+ end
36
+
37
+ def table(value)
38
+ self.instance_variable_set(:@table, value)
39
+ end
40
+
41
+ def partition_key(field, type)
42
+ self.instance_variable_set(:@partition_key, { field: field, type: type })
43
+ end
44
+
45
+ def range_key(field, type)
46
+ self.instance_variable_set(:@range_key, { field: field, type: type })
47
+ end
48
+
49
+ def create(store:, read_capacity: 25, write_capacity: 25)
50
+ unless self.instance_variable_defined?(:@table)
51
+ raise DynamoDbFramework::Index::InvalidConfigException.new('Table must be specified.')
52
+ end
53
+ table = self.instance_variable_get(:@table)
54
+ table_name = table.config[:table_name]
55
+
56
+ unless self.instance_variable_defined?(:@partition_key)
57
+ raise DynamoDbFramework::Index::InvalidConfigException.new('Partition key must be specified.')
58
+ end
59
+ partition_key = self.instance_variable_get(:@partition_key)
60
+
61
+ if self.instance_variable_defined?(:@range_key)
62
+ range_key = self.instance_variable_get(:@range_key)
63
+ end
64
+
65
+ builder = DynamoDbFramework::AttributesBuilder.new
66
+ builder.add({ name: partition_key[:field], type: partition_key[:type], key: :partition })
67
+ if range_key != nil
68
+ builder.add({ name: range_key[:field], type: range_key[:type], key: :range })
69
+ end
70
+ if partition_key[:field] != table.config[:partition_key][:field] && range_key != nil && range_key[:field] != table.config[:partition_key][:field]
71
+ builder.add({ name: table.config[:partition_key][:field], type: table.config[:partition_key][:type] })
72
+ end
73
+ if table.config[:range_key] != nil && partition_key[:field] != table.config[:range_key][:field] && range_key != nil && range_key[:field] != table.config[:range_key][:field]
74
+ builder.add({ name: table.config[:range_key][:field], type: table.config[:range_key][:type] })
75
+ end
76
+
77
+ table_manager = DynamoDbFramework::TableManager.new(store)
78
+
79
+ range_key_field = range_key[:field] unless range_key == nil
80
+
81
+ index =table_manager.create_global_index(full_index_name, partition_key[:field], range_key_field, read_capacity, write_capacity)
82
+
83
+ table_manager.add_index(table_name, builder.attributes, index)
84
+ end
85
+
86
+ def update(store:, read_capacity:, write_capacity:)
87
+ unless self.instance_variable_defined?(:@table)
88
+ raise DynamoDbFramework::Index::InvalidConfigException.new('Table must be specified.')
89
+ end
90
+ table = self.instance_variable_get(:@table)
91
+
92
+ DynamoDbFramework::TableManager.new(store).update_index_throughput(table.config[:table_name], full_index_name, read_capacity, write_capacity)
93
+ end
94
+
95
+ def drop(store:)
96
+ unless self.instance_variable_defined?(:@table)
97
+ raise DynamoDbFramework::Index::InvalidConfigException.new('Table must be specified.')
98
+ end
99
+ table = self.instance_variable_get(:@table)
100
+ table_name = table.config[:table_name]
101
+
102
+ DynamoDbFramework::TableManager.new(store).drop_index(table_name, full_index_name)
103
+ end
104
+
105
+ def exists?(store:)
106
+ unless self.instance_variable_defined?(:@table)
107
+ raise DynamoDbFramework::Index::InvalidConfigException.new('Table must be specified.')
108
+ end
109
+ table = self.instance_variable_get(:@table)
110
+ table_name = table.config[:table_name]
111
+ DynamoDbFramework::TableManager.new(store).has_index?(table_name, full_index_name)
112
+ end
113
+
114
+ def query(partition:)
115
+ DynamoDbFramework::Query.new(index_name: config[:index_name], table_name: config[:table].config[:table_name], partition_key: config[:partition_key][:field], partition_value: partition)
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,111 @@
1
+ module DynamoDbFramework
2
+ class Query
3
+
4
+ def initialize(table_name:, partition_key:, partition_value:, index_name: nil)
5
+ @table_name = table_name
6
+ @partition_key = partition_key
7
+ @partition_value = partition_value
8
+ @index_name = index_name
9
+ @parts = []
10
+ end
11
+
12
+ def method_missing(name)
13
+ @parts << { type: :field, value: name }
14
+ self
15
+ end
16
+
17
+ def eq(value)
18
+ condition(expression: '==', value: value)
19
+ self
20
+ end
21
+
22
+ def not_eq(value)
23
+ condition(expression: '!=', value: value)
24
+ self
25
+ end
26
+
27
+ def gt(value)
28
+ condition(expression: '>', value: value)
29
+ self
30
+ end
31
+
32
+ def gt_eq(value)
33
+ condition(expression: '>=', value: value)
34
+ self
35
+ end
36
+
37
+ def lt(value)
38
+ condition(expression: '<', value: value)
39
+ self
40
+ end
41
+
42
+ def lt_eq(value)
43
+ condition(expression: '<=', value: value)
44
+ self
45
+ end
46
+
47
+ def and
48
+ @parts << { type: :and }
49
+ self
50
+ end
51
+
52
+ def or
53
+ @parts << { type: :or }
54
+ self
55
+ end
56
+
57
+ def execute(store:, limit: nil, count: false)
58
+ build
59
+ repository = DynamoDbFramework::Repository.new(store)
60
+ repository.table_name = @table_name
61
+ repository.query(@partition_key, @partition_value, nil, nil, @expression_string, @expression_params, @index_name, limit, count)
62
+ end
63
+
64
+ def build
65
+ @expression_string = ''
66
+ @expression_params = {}
67
+
68
+ counter = 0
69
+ @parts.each do |p|
70
+ case p[:type]
71
+ when :field
72
+ field_param = '#' + p[:value].to_s
73
+ @expression_string += ' ' + field_param
74
+ @expression_params[field_param] = p[:value].to_s
75
+ when :condition
76
+ param_name = ':p' + counter.to_s
77
+ counter = counter + 1
78
+ @expression_string += ' ' + p[:expression].to_s + ' ' + param_name
79
+ @expression_params[param_name] = clean_value(p[:value])
80
+ when :and
81
+ @expression_string += ' and'
82
+ when :or
83
+ @expression_string += ' or'
84
+ else
85
+ raise 'Invalid query part'
86
+ end
87
+ end
88
+
89
+ return @expression_string.strip, @expression_params
90
+ end
91
+
92
+ def condition(expression:, value:)
93
+ @parts << { type: :condition, expression: expression, value: value }
94
+ end
95
+
96
+ def convert_date(value)
97
+ klass = value.class
98
+ return value.iso8601 if klass == DateTime
99
+ return value.to_i if klass == Time
100
+ end
101
+
102
+ def clean_value(value)
103
+ if value.is_a?(Time) || value.is_a?(DateTime)
104
+ convert_date(value)
105
+ else
106
+ value
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -47,12 +47,34 @@ module DynamoDbFramework
47
47
 
48
48
  end
49
49
 
50
+ def delete_item(partition_key:, partition_key_value:, range_key: nil, range_key_value: nil)
51
+
52
+ keys = {
53
+ partition_key.to_s => clean_value(partition_key_value)
54
+ }
55
+
56
+ if range_key != nil
57
+ keys[range_key.to_s] = clean_value(range_key_value)
58
+ end
59
+
60
+ params =
61
+ {
62
+ table_name: @table_name,
63
+ key: keys
64
+ }
65
+
66
+ dynamodb.client.delete_item(params)
67
+
68
+ return true
69
+
70
+ end
71
+
50
72
  def get_by_key(partition_key, partition_value, range_key = nil, range_value = nil)
51
73
 
52
74
  key = {}
53
- key[partition_key] = partition_value
75
+ key[partition_key] = clean_value(partition_value)
54
76
  if(range_key != nil)
55
- key[range_key] = range_value
77
+ key[range_key] = clean_value(range_value)
56
78
  end
57
79
 
58
80
  params = {
@@ -99,7 +121,7 @@ module DynamoDbFramework
99
121
  if key[0] == '#'
100
122
  params[:expression_attribute_names][key] = value
101
123
  elsif key[0] == ':'
102
- params[:expression_attribute_values][key] = value
124
+ params[:expression_attribute_values][key] = clean_value(value)
103
125
  end
104
126
  end
105
127
 
@@ -147,19 +169,19 @@ module DynamoDbFramework
147
169
  if range_key_name != nil
148
170
  params[:key_condition_expression] = '#partition_key = :partition_key and #range_key = :range_key'
149
171
  params[:expression_attribute_names] = { '#partition_key' => partition_key_name, '#range_key' => range_key_name }
150
- params[:expression_attribute_values] = { ':partition_key' => partition_key_value, ':range_key' => range_key_value }
172
+ params[:expression_attribute_values] = { ':partition_key' => clean_value(partition_key_value), ':range_key' => clean_value(range_key_value) }
151
173
  else
152
174
  params[:key_condition_expression] = '#partition_key = :partition_key'
153
175
  params[:expression_attribute_names] = { '#partition_key' => partition_key_name }
154
- params[:expression_attribute_values] = { ':partition_key' => partition_key_value }
176
+ params[:expression_attribute_values] = { ':partition_key' => clean_value(partition_key_value) }
155
177
  end
156
178
 
157
179
  if expression_params != nil
158
180
  expression_params.each do |key, value|
159
181
  if key[0] == '#'
160
- params[:expression_attribute_names][key] = value
182
+ params[:expression_attribute_names][key] = clean_value(value)
161
183
  elsif key[0] == ':'
162
- params[:expression_attribute_values][key] = value
184
+ params[:expression_attribute_values][key] = clean_value(value)
163
185
  end
164
186
  end
165
187
 
@@ -222,5 +244,13 @@ module DynamoDbFramework
222
244
  return value.iso8601 if klass == DateTime
223
245
  return value.to_i if klass == Time
224
246
  end
247
+
248
+ def clean_value(value)
249
+ if value.is_a?(Time) || value.is_a?(DateTime)
250
+ convert_date(value)
251
+ else
252
+ value
253
+ end
254
+ end
225
255
  end
226
256
  end
@@ -0,0 +1,114 @@
1
+ module DynamoDbFramework
2
+ module Table
3
+
4
+ class InvalidConfigException < StandardError
5
+ def initialize(message)
6
+ super(message)
7
+ end
8
+ end
9
+
10
+ def config
11
+ details = {
12
+ table_name: full_table_name,
13
+ partition_key: self.instance_variable_get(:@partition_key)
14
+ }
15
+ if self.instance_variable_defined?(:@range_key)
16
+ details[:range_key] = self.instance_variable_get(:@range_key)
17
+ end
18
+ details
19
+ end
20
+
21
+ def table_name(value)
22
+ self.instance_variable_set(:@table_name, value)
23
+ end
24
+
25
+ def full_table_name
26
+ unless self.instance_variable_defined?(:@table_name)
27
+ raise DynamoDbFramework::Table::InvalidConfigException.new('Table name must be specified.')
28
+ end
29
+ table_name = self.instance_variable_get(:@table_name)
30
+ if DynamoDbFramework.namespace != nil
31
+ table_name = "#{DynamoDbFramework.namespace}#{DynamoDbFramework.namespace_delimiter}#{table_name}"
32
+ end
33
+ table_name
34
+ end
35
+
36
+ def partition_key(field, type)
37
+ self.instance_variable_set(:@partition_key, { field: field, type: type })
38
+ end
39
+
40
+ def range_key(field, type)
41
+ self.instance_variable_set(:@range_key, { field: field, type: type })
42
+ end
43
+
44
+ def create(store:, read_capacity: 25, write_capacity: 25)
45
+ unless self.instance_variable_defined?(:@partition_key)
46
+ raise DynamoDbFramework::Table::InvalidConfigException.new('Partition key must be specified.')
47
+ end
48
+ partition_key = self.instance_variable_get(:@partition_key)
49
+
50
+ if self.instance_variable_defined?(:@range_key)
51
+ range_key = self.instance_variable_get(:@range_key)
52
+ end
53
+
54
+ builder = DynamoDbFramework::AttributesBuilder.new
55
+ builder.add({ name: partition_key[:field], type: partition_key[:type], key: :partition })
56
+ if range_key != nil
57
+ builder.add({ name: range_key[:field], type: range_key[:type], key: :range })
58
+ end
59
+
60
+ DynamoDbFramework::TableManager.new(store).create_table({ name: full_table_name, attributes: builder.attributes, read_capacity: read_capacity, write_capacity: write_capacity })
61
+ end
62
+
63
+ def update(store:, read_capacity:, write_capacity:)
64
+ DynamoDbFramework::TableManager.new(store).update_throughput(full_table_name, read_capacity, write_capacity)
65
+ end
66
+
67
+ def drop(store:)
68
+ DynamoDbFramework::TableManager.new(store).drop(full_table_name)
69
+ end
70
+
71
+ def exists?(store:)
72
+ DynamoDbFramework::TableManager.new(store).exists?(full_table_name)
73
+ end
74
+
75
+ def query(partition:)
76
+ DynamoDbFramework::Query.new(table_name: config[:table_name], partition_key: config[:partition_key][:field], partition_value: partition)
77
+ end
78
+
79
+ def all(store:)
80
+ repository = DynamoDbFramework::Repository.new(store)
81
+ repository.table_name = config[:table_name]
82
+ repository.all
83
+ end
84
+
85
+ def put_item(store:, item:)
86
+ repository = DynamoDbFramework::Repository.new(store)
87
+ repository.table_name = config[:table_name]
88
+ repository.put(item)
89
+ end
90
+
91
+ def get_item(store:, partition:, range: nil)
92
+ repository = DynamoDbFramework::Repository.new(store)
93
+ repository.table_name = config[:table_name]
94
+
95
+ if range != nil
96
+ repository.get_by_key(config[:partition_key][:field], partition, config[:range_key][:field], range)
97
+ else
98
+ repository.get_by_key(config[:partition_key][:field], partition)
99
+ end
100
+ end
101
+
102
+ def delete_item(store:, partition:, range: nil)
103
+ repository = DynamoDbFramework::Repository.new(store)
104
+ repository.table_name = config[:table_name]
105
+
106
+ if range != nil
107
+ range_key = config[:range_key][:field]
108
+ end
109
+
110
+ repository.delete_item(partition_key: config[:partition_key][:field], partition_key_value: partition, range_key: range_key, range_key_value: range)
111
+ end
112
+
113
+ end
114
+ end
@@ -1,3 +1,3 @@
1
1
  module DynamoDbFramework
2
- VERSION = '1.3.0'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -1,3 +1,5 @@
1
+ require 'hash_kit'
2
+ require 'json'
1
3
  require_relative 'dynamodb_framework/version'
2
4
  require_relative 'dynamodb_framework/dynamodb_attributes_builder'
3
5
  require_relative 'dynamodb_framework/dynamodb_store'
@@ -8,5 +10,23 @@ require_relative 'dynamodb_framework/dynamodb_migration_script'
8
10
  require_relative 'dynamodb_framework/dynamodb_logger'
9
11
  require_relative 'dynamodb_framework/hash_helper'
10
12
  require_relative 'dynamodb_framework/dynamodb_namespace_migration_manager'
13
+ require_relative 'dynamodb_framework/dynamodb_table'
14
+ require_relative 'dynamodb_framework/dynamodb_index'
15
+ require_relative 'dynamodb_framework/dynamodb_query'
11
16
 
12
17
  require 'date'
18
+
19
+ module DynamoDbFramework
20
+ def self.namespace=(value)
21
+ @namespace = value
22
+ end
23
+ def self.namespace
24
+ @namespace
25
+ end
26
+ def self.namespace_delimiter=(value)
27
+ @namespace_delimiter = value
28
+ end
29
+ def self.namespace_delimiter
30
+ @namespace_delimiter ||= '.'
31
+ end
32
+ end
@@ -0,0 +1,212 @@
1
+ RSpec.describe DynamoDbFramework::Index do
2
+
3
+ let(:store) do
4
+ DynamoDbFramework::Store.new({ endpoint: DYNAMODB_STORE_ENDPOINT, aws_region: 'eu-west-1' })
5
+ end
6
+
7
+ let(:table_manager) do
8
+ DynamoDbFramework::TableManager.new(store)
9
+ end
10
+
11
+ let(:table_name) { ExampleTable.config[:table_name] }
12
+
13
+ before do
14
+ ExampleTable.create(store: store)
15
+ end
16
+
17
+ describe '#create' do
18
+ context 'when a valid index class calls the create method' do
19
+ context 'with a range key' do
20
+ let(:index_name) { ExampleIndex.config[:index_name] }
21
+ before do
22
+ if table_manager.has_index?(table_name, index_name)
23
+ table_manager.drop_index(table_name, index_name)
24
+ end
25
+ end
26
+ it 'should create the index' do
27
+ expect(table_manager.has_index?(table_name, index_name)).to be false
28
+ ExampleIndex.create(store: store)
29
+ expect(table_manager.has_index?(table_name, index_name)).to be true
30
+ end
31
+ end
32
+
33
+ context 'without a range key' do
34
+ let(:index_name) { ExampleIndexWithoutRangeKey.config[:index_name] }
35
+ before do
36
+ if table_manager.has_index?(table_name, index_name)
37
+ table_manager.drop_index(table_name, index_name)
38
+ end
39
+ end
40
+ it 'should create the table' do
41
+ expect(table_manager.has_index?(table_name, index_name)).to be false
42
+ ExampleIndexWithoutRangeKey.create(store: store)
43
+ expect(table_manager.has_index?(table_name, index_name)).to be true
44
+ end
45
+ end
46
+ end
47
+ context 'when an invalid index class calls the create method' do
48
+ context 'without a index_name specified' do
49
+ it 'should raise an exception' do
50
+ expect{ ExampleIndexWithoutIndexName.create(store: store) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
51
+ end
52
+ end
53
+ context 'without a table specified' do
54
+ it 'should raise an exception' do
55
+ expect{ ExampleIndexWithoutTable.create(store: store) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
56
+ end
57
+ end
58
+ context 'without a partition_key specified' do
59
+ it 'should raise an exception' do
60
+ expect{ ExampleIndexWithoutPartitionKey.create(store: store) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#update' do
67
+ context 'when a valid index class calls the update method' do
68
+ let(:index_name) { ExampleIndex.config[:index_name] }
69
+ before do
70
+ if table_manager.has_index?(table_name, index_name)
71
+ table_manager.drop_index(table_name, index_name)
72
+ end
73
+ ExampleIndex.create(store: store)
74
+ end
75
+ it 'should update the index' do
76
+ ExampleIndex.update(store: store, read_capacity: 50, write_capacity: 50)
77
+ end
78
+ end
79
+ context 'when an invalid index class calls the update method' do
80
+ context 'without an index_name specified' do
81
+ it 'should raise an exception' do
82
+ expect{ ExampleIndexWithoutIndexName.update(store: store, read_capacity: 50, write_capacity: 50) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
83
+ end
84
+ end
85
+ context 'without a table specified' do
86
+ it 'should raise an exception' do
87
+ expect{ ExampleIndexWithoutTable.update(store: store, read_capacity: 50, write_capacity: 50) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ describe '#drop' do
94
+ context 'when a valid index class calls the drop method' do
95
+ let(:index_name) { ExampleIndex.config[:index_name] }
96
+ before do
97
+ if table_manager.has_index?(table_name, index_name)
98
+ table_manager.drop_index(table_name, index_name)
99
+ end
100
+ ExampleIndex.create(store: store)
101
+ end
102
+ it 'should drop the index' do
103
+ ExampleIndex.drop(store: store)
104
+ expect(table_manager.has_index?(table_name, index_name)).to be false
105
+ end
106
+ end
107
+ context 'when an invalid index class calls the drop method' do
108
+ context 'without an index_name specified' do
109
+ it 'should raise an exception' do
110
+ expect{ ExampleIndexWithoutIndexName.drop(store: store) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
111
+ end
112
+ end
113
+ context 'without a table specified' do
114
+ it 'should raise an exception' do
115
+ expect{ ExampleIndexWithoutTable.drop(store: store) }.to raise_error(DynamoDbFramework::Index::InvalidConfigException)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ describe '#exists?' do
122
+ context 'when the index exists' do
123
+ let(:index_name) { ExampleIndex.config[:index_name] }
124
+ before do
125
+ if table_manager.has_index?(table_name, index_name)
126
+ table_manager.drop_index(table_name, index_name)
127
+ end
128
+ ExampleIndex.create(store: store)
129
+ end
130
+ it 'should return true' do
131
+ expect(ExampleIndex.exists?(store: store)).to be true
132
+ end
133
+ end
134
+ context 'when the index does NOT exist' do
135
+ let(:index_name) { ExampleIndex.config[:index_name] }
136
+ before do
137
+ if table_manager.has_index?(table_name, index_name)
138
+ table_manager.drop_index(table_name, index_name)
139
+ end
140
+ end
141
+ it 'should return false' do
142
+ expect(ExampleIndex.exists?(store: store)).to be false
143
+ end
144
+ end
145
+ end
146
+
147
+ describe '#query' do
148
+
149
+ let(:repository) do
150
+ DynamoDbFramework::Repository.new(store)
151
+ end
152
+
153
+ let(:table_name) { ExampleTable.config[:table_name] }
154
+ let(:index_name) { ExampleIndex.config[:index_name] }
155
+
156
+ def create_query_item(name, number)
157
+ item = TestItem.new
158
+ item.id = SecureRandom.uuid
159
+ item.name = name
160
+ item.timestamp = Time.now
161
+ item.number = number
162
+ repository.table_name = table_name
163
+ repository.put(item)
164
+ end
165
+
166
+ before do
167
+ table_manager.drop(table_name)
168
+ ExampleTable.create(store: store)
169
+ ExampleIndex.create(store: store)
170
+
171
+ create_query_item('name 1', 1)
172
+ create_query_item('name 1', 2)
173
+ create_query_item('name 1', 3)
174
+ create_query_item('name 1', 4)
175
+ create_query_item('name 2', 1)
176
+ create_query_item('name 2', 2)
177
+ create_query_item('name 2', 3)
178
+ create_query_item('name 3', 1)
179
+ create_query_item('name 3', 2)
180
+ end
181
+
182
+ it 'should return the expected items' do
183
+ results = ExampleIndex.query(partition: 'name 1')
184
+ .number.gt_eq(1)
185
+ .and
186
+ .number.lt_eq(5)
187
+ .execute(store: store)
188
+ expect(results.length).to eq 4
189
+ end
190
+ context 'when limit is specified' do
191
+ it 'should return the expected items' do
192
+ results = ExampleIndex.query(partition: 'name 1')
193
+ .number.gt_eq(1)
194
+ .and
195
+ .number.lt_eq(5)
196
+ .execute(store: store, limit: 1)
197
+ expect(results.length).to eq 1
198
+ end
199
+ end
200
+ context 'when count is specified' do
201
+ it 'should return the expected count' do
202
+ count = ExampleIndex.query(partition: 'name 1')
203
+ .number.gt_eq(1)
204
+ .and
205
+ .number.lt_eq(5)
206
+ .execute(store: store, count: 4)
207
+ expect(count).to eq 4
208
+ end
209
+ end
210
+ end
211
+
212
+ end