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.
- checksums.yaml +4 -4
- data/lib/dynamodb_framework/dynamodb_index.rb +119 -0
- data/lib/dynamodb_framework/dynamodb_query.rb +111 -0
- data/lib/dynamodb_framework/dynamodb_repository.rb +37 -7
- data/lib/dynamodb_framework/dynamodb_table.rb +114 -0
- data/lib/dynamodb_framework/version.rb +1 -1
- data/lib/dynamodb_framework.rb +20 -0
- data/spec/dynamodb_index_spec.rb +212 -0
- data/spec/dynamodb_migration_manager_spec.rb +134 -0
- data/spec/dynamodb_namespace_migration_manager_spec.rb +134 -0
- data/spec/dynamodb_query_spec.rb +87 -0
- data/spec/dynamodb_repository_spec.rb +306 -0
- data/spec/dynamodb_table_manager_spec.rb +156 -0
- data/spec/dynamodb_table_spec.rb +245 -0
- data/spec/example_index.rb +45 -0
- data/spec/example_table.rb +41 -0
- data/spec/hash_helper_spec.rb +129 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/test_item.rb +6 -0
- data/spec/test_migration_script1.rb +24 -0
- data/spec/test_migration_script2.rb +24 -0
- metadata +35 -25
- data/.gitignore +0 -14
- data/.idea/.name +0 -1
- data/.idea/.rakeTasks +0 -7
- data/.idea/encodings.xml +0 -6
- data/.idea/misc.xml +0 -33
- data/.idea/modules.xml +0 -8
- data/.idea/vcs.xml +0 -6
- data/.rspec +0 -3
- data/CODE_OF_CONDUCT.md +0 -49
- data/Gemfile +0 -12
- data/LICENSE.txt +0 -21
- data/README.md +0 -394
- data/Rakefile +0 -2
- data/dynamodb_framework.gemspec +0 -29
- data/script/cleanup.sh +0 -6
- data/script/container_loop.sh +0 -6
- data/script/docker-compose.yml +0 -5
- data/script/restart.sh +0 -3
- data/script/start.sh +0 -4
- data/script/stop.sh +0 -2
- data/yard.sh +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9c4bf2ce79200a61fab5ff066efb0de60535fdd8
|
4
|
+
data.tar.gz: 5c338f2559cadf5e0e2d22b83ef2e71afac33b2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/dynamodb_framework.rb
CHANGED
@@ -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
|