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.
- 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
|