dynamodb_framework 1.3.0 → 1.4.0

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