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
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe DynamoDbFramework::TableManager do
|
4
|
+
|
5
|
+
let(:store) do
|
6
|
+
DynamoDbFramework::Store.new({ endpoint: DYNAMODB_STORE_ENDPOINT, aws_region: 'eu-west-1' })
|
7
|
+
end
|
8
|
+
|
9
|
+
subject do
|
10
|
+
DynamoDbFramework::TableManager.new(store)
|
11
|
+
end
|
12
|
+
|
13
|
+
attributes_builder = DynamoDbFramework::AttributesBuilder.new
|
14
|
+
|
15
|
+
before do
|
16
|
+
attributes_builder = DynamoDbFramework::AttributesBuilder.new
|
17
|
+
attributes_builder.add(:id, :S)
|
18
|
+
|
19
|
+
subject.create('update_throughput', attributes_builder.attributes, :id)
|
20
|
+
|
21
|
+
subject.create('add_index', attributes_builder.attributes, :id)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can create, check exists & drop tables' do
|
25
|
+
|
26
|
+
exists = subject.exists?('create_drop_test')
|
27
|
+
|
28
|
+
if exists
|
29
|
+
subject.drop('create_drop_test')
|
30
|
+
end
|
31
|
+
|
32
|
+
subject.create('create_drop_test', attributes_builder.attributes, :id)
|
33
|
+
|
34
|
+
subject.drop('create_drop_test')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'can create a table with a hash key and a range key' do
|
38
|
+
|
39
|
+
exists = subject.exists?('dual_key')
|
40
|
+
|
41
|
+
if exists
|
42
|
+
subject.drop('dual_key')
|
43
|
+
end
|
44
|
+
|
45
|
+
subject.create('dual_key', attributes_builder.attributes, :id)
|
46
|
+
|
47
|
+
subject.drop('dual_key')
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'can create a table with a global secondary index' do
|
52
|
+
|
53
|
+
exists = subject.exists?('index_test')
|
54
|
+
|
55
|
+
if exists
|
56
|
+
subject.drop('index_test')
|
57
|
+
end
|
58
|
+
|
59
|
+
global_indexes = []
|
60
|
+
index1 = subject.create_global_index('index1', :name, :number)
|
61
|
+
global_indexes.push(index1)
|
62
|
+
|
63
|
+
builder = DynamoDbFramework::AttributesBuilder.new
|
64
|
+
builder.add(:id, :S)
|
65
|
+
builder.add(:name, :S)
|
66
|
+
builder.add(:number, :N)
|
67
|
+
|
68
|
+
subject.create('index_test', builder.attributes, :id, nil, 20, 10, global_indexes)
|
69
|
+
|
70
|
+
subject.drop('index_test')
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'can update the throughput of a table' do
|
75
|
+
|
76
|
+
subject.update_throughput('update_throughput', 30, 30)
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'can add a global index to an existing table' do
|
81
|
+
|
82
|
+
builder = DynamoDbFramework::AttributesBuilder.new
|
83
|
+
builder.add(:id, :S)
|
84
|
+
builder.add(:name, :S)
|
85
|
+
|
86
|
+
index = subject.create_global_index('new_index', :name, nil)
|
87
|
+
subject.add_index('add_index', builder.attributes, index)
|
88
|
+
|
89
|
+
has_index = subject.has_index?('add_index', 'new_index')
|
90
|
+
|
91
|
+
expect(has_index).to eq(true)
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'can update the throughput of a global secondary index' do
|
96
|
+
|
97
|
+
exists = subject.exists?('update_index_throughput_test')
|
98
|
+
|
99
|
+
if exists
|
100
|
+
subject.drop('update_index_throughput_test')
|
101
|
+
end
|
102
|
+
|
103
|
+
global_indexes = []
|
104
|
+
index1 = subject.create_global_index('index1', :name, :number)
|
105
|
+
global_indexes.push(index1)
|
106
|
+
|
107
|
+
builder = DynamoDbFramework::AttributesBuilder.new
|
108
|
+
builder.add(:id, :S)
|
109
|
+
builder.add(:name, :S)
|
110
|
+
builder.add(:number, :N)
|
111
|
+
|
112
|
+
subject.create('update_index_throughput_test', builder.attributes, :id, nil, 20, 10, global_indexes)
|
113
|
+
|
114
|
+
subject.update_index_throughput('update_index_throughput_test', 'index1', 50, 50)
|
115
|
+
|
116
|
+
subject.drop('update_index_throughput_test')
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'can drop an existing global secondary index' do
|
121
|
+
|
122
|
+
exists = subject.exists?('drop_index_test')
|
123
|
+
|
124
|
+
if exists
|
125
|
+
subject.drop('drop_index_test')
|
126
|
+
end
|
127
|
+
|
128
|
+
global_indexes = []
|
129
|
+
index1 = subject.create_global_index('index1', :name, :number)
|
130
|
+
global_indexes.push(index1)
|
131
|
+
|
132
|
+
builder = DynamoDbFramework::AttributesBuilder.new
|
133
|
+
builder.add(:id, :S)
|
134
|
+
builder.add(:name, :S)
|
135
|
+
builder.add(:number, :N)
|
136
|
+
|
137
|
+
subject.create('drop_index_test', builder.attributes, :id, nil, 20, 10, global_indexes)
|
138
|
+
|
139
|
+
subject.drop_index('drop_index_test', 'index1')
|
140
|
+
|
141
|
+
has_index = subject.has_index?('drop_index_test', 'index1')
|
142
|
+
|
143
|
+
subject.drop('drop_index_test')
|
144
|
+
|
145
|
+
expect(has_index).to eq(false)
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
after do
|
150
|
+
|
151
|
+
subject.drop('update_throughput')
|
152
|
+
subject.drop('add_index')
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
RSpec.describe DynamoDbFramework::Table 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
|
+
describe '#create' do
|
12
|
+
context 'when a valid table class calls the create method' do
|
13
|
+
context 'with a range key' do
|
14
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
15
|
+
before do
|
16
|
+
table_manager.drop(table_name)
|
17
|
+
end
|
18
|
+
it 'should create the table' do
|
19
|
+
expect(table_manager.exists?(table_name)).to be false
|
20
|
+
ExampleTable.create(store: store)
|
21
|
+
expect(table_manager.exists?(table_name)).to be true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'without a range key' do
|
26
|
+
let(:table_name) { ExampleTableWithoutRangeKey.config[:table_name] }
|
27
|
+
before do
|
28
|
+
table_manager.drop(table_name)
|
29
|
+
end
|
30
|
+
it 'should create the table' do
|
31
|
+
expect(table_manager.exists?(table_name)).to be false
|
32
|
+
ExampleTableWithoutRangeKey.create(store: store)
|
33
|
+
expect(table_manager.exists?(table_name)).to be true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
context 'when an invalid table class calls the create method' do
|
38
|
+
context 'without a table_name specified' do
|
39
|
+
it 'should raise an exception' do
|
40
|
+
expect{ ExampleTableWithoutTableName.create(store: store) }.to raise_error(DynamoDbFramework::Table::InvalidConfigException)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
context 'without a partition_key specified' do
|
44
|
+
it 'should raise an exception' do
|
45
|
+
expect{ ExampleTableWithoutPartitionKey.create(store: store) }.to raise_error(DynamoDbFramework::Table::InvalidConfigException)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#update' do
|
52
|
+
context 'when a valid table class calls the update method' do
|
53
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
54
|
+
before do
|
55
|
+
table_manager.drop(table_name)
|
56
|
+
ExampleTable.create(store: store)
|
57
|
+
end
|
58
|
+
it 'should update the table' do
|
59
|
+
ExampleTable.update(store: store, read_capacity: 50, write_capacity: 50)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
context 'when an invalid table class calls the update method' do
|
63
|
+
context 'without a table_name specified' do
|
64
|
+
it 'should raise an exception' do
|
65
|
+
expect{ ExampleTableWithoutTableName.update(store: store, read_capacity: 50, write_capacity: 50) }.to raise_error(DynamoDbFramework::Table::InvalidConfigException)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#drop' do
|
72
|
+
context 'when a valid table class calls the drop method' do
|
73
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
74
|
+
before do
|
75
|
+
table_manager.drop(table_name)
|
76
|
+
ExampleTable.create(store: store)
|
77
|
+
end
|
78
|
+
it 'should drop the table' do
|
79
|
+
ExampleTable.drop(store: store)
|
80
|
+
expect(table_manager.exists?(table_name)).to be false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
context 'when an invalid table class calls the drop method' do
|
84
|
+
context 'without a table_name specified' do
|
85
|
+
it 'should raise an exception' do
|
86
|
+
expect{ ExampleTableWithoutTableName.drop(store: store) }.to raise_error(DynamoDbFramework::Table::InvalidConfigException)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#exists?' do
|
93
|
+
context 'when a table already exists' do
|
94
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
95
|
+
before do
|
96
|
+
table_manager.drop(table_name)
|
97
|
+
ExampleTable.create(store: store)
|
98
|
+
end
|
99
|
+
it 'should return true' do
|
100
|
+
expect(ExampleTable.exists?(store: store)).to be true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
context 'when a table does NOT already exist' do
|
104
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
105
|
+
before do
|
106
|
+
table_manager.drop(table_name)
|
107
|
+
end
|
108
|
+
it 'should return false' do
|
109
|
+
expect(ExampleTable.exists?(store: store)).to be false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '#query' do
|
115
|
+
|
116
|
+
let(:repository) do
|
117
|
+
DynamoDbFramework::Repository.new(store)
|
118
|
+
end
|
119
|
+
|
120
|
+
let(:table_name) { ExampleTable2.config[:table_name] }
|
121
|
+
|
122
|
+
def create_query_item(name, number)
|
123
|
+
item = TestItem.new
|
124
|
+
item.id = SecureRandom.uuid
|
125
|
+
item.name = name
|
126
|
+
item.timestamp = Time.now
|
127
|
+
item.number = number
|
128
|
+
repository.table_name = table_name
|
129
|
+
repository.put(item)
|
130
|
+
end
|
131
|
+
|
132
|
+
before do
|
133
|
+
table_manager.drop(table_name)
|
134
|
+
ExampleTable2.create(store: store)
|
135
|
+
|
136
|
+
create_query_item('name 1', 1)
|
137
|
+
create_query_item('name 1', 2)
|
138
|
+
create_query_item('name 1', 3)
|
139
|
+
create_query_item('name 1', 4)
|
140
|
+
create_query_item('name 2', 1)
|
141
|
+
create_query_item('name 2', 2)
|
142
|
+
create_query_item('name 2', 3)
|
143
|
+
create_query_item('name 3', 1)
|
144
|
+
create_query_item('name 3', 2)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should return the expected items' do
|
148
|
+
results = ExampleTable2.query(partition: 'name 1')
|
149
|
+
.number.gt_eq(1)
|
150
|
+
.and
|
151
|
+
.number.lt_eq(5)
|
152
|
+
.execute(store: store)
|
153
|
+
expect(results.length).to eq 4
|
154
|
+
end
|
155
|
+
context 'when limit is specified' do
|
156
|
+
it 'should return the expected items' do
|
157
|
+
results = ExampleTable2.query(partition: 'name 1')
|
158
|
+
.number.gt_eq(1)
|
159
|
+
.and
|
160
|
+
.number.lt_eq(5)
|
161
|
+
.execute(store: store, limit: 1)
|
162
|
+
expect(results.length).to eq 1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
context 'when count is specified' do
|
166
|
+
it 'should return the expected count' do
|
167
|
+
count = ExampleTable2.query(partition: 'name 1')
|
168
|
+
.number.gt_eq(1)
|
169
|
+
.and
|
170
|
+
.number.lt_eq(5)
|
171
|
+
.execute(store: store, count: 4)
|
172
|
+
expect(count).to eq 4
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '#all' do
|
178
|
+
let(:repository) do
|
179
|
+
DynamoDbFramework::Repository.new(store)
|
180
|
+
end
|
181
|
+
|
182
|
+
let(:table_name) { ExampleTable2.config[:table_name] }
|
183
|
+
|
184
|
+
def create_query_item(name, number)
|
185
|
+
item = TestItem.new
|
186
|
+
item.id = SecureRandom.uuid
|
187
|
+
item.name = name
|
188
|
+
item.timestamp = Time.now
|
189
|
+
item.number = number
|
190
|
+
repository.table_name = table_name
|
191
|
+
repository.put(item)
|
192
|
+
end
|
193
|
+
|
194
|
+
before do
|
195
|
+
table_manager.drop(table_name)
|
196
|
+
ExampleTable2.create(store: store)
|
197
|
+
|
198
|
+
create_query_item('name 1', 1)
|
199
|
+
create_query_item('name 1', 2)
|
200
|
+
create_query_item('name 1', 3)
|
201
|
+
create_query_item('name 1', 4)
|
202
|
+
create_query_item('name 2', 1)
|
203
|
+
create_query_item('name 2', 2)
|
204
|
+
create_query_item('name 2', 3)
|
205
|
+
create_query_item('name 3', 1)
|
206
|
+
create_query_item('name 3', 2)
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should return all items' do
|
210
|
+
results = ExampleTable2.all(store: store)
|
211
|
+
expect(results.length).to eq 9
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '#CRUD' do
|
216
|
+
let(:repository) do
|
217
|
+
DynamoDbFramework::Repository.new(store)
|
218
|
+
end
|
219
|
+
|
220
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
221
|
+
|
222
|
+
let(:item) do
|
223
|
+
item = TestItem.new
|
224
|
+
item.id = SecureRandom.uuid
|
225
|
+
item.name = 'abc'
|
226
|
+
item.timestamp = Time.now
|
227
|
+
item.number = 1
|
228
|
+
item
|
229
|
+
end
|
230
|
+
|
231
|
+
let(:table_name) { ExampleTable.config[:table_name] }
|
232
|
+
before do
|
233
|
+
table_manager.drop(table_name)
|
234
|
+
ExampleTable.create(store: store)
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should add, get and delete the item to the table' do
|
238
|
+
ExampleTable.put_item(store: store, item: item)
|
239
|
+
expect(ExampleTable.get_item(store: store, partition: item.id, range: item.timestamp)).not_to be_nil
|
240
|
+
ExampleTable.delete_item(store: store, partition: item.id, range: item.timestamp)
|
241
|
+
expect(ExampleTable.get_item(store: store, partition: item.id, range: item.timestamp)).to be_nil
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class ExampleIndex
|
2
|
+
extend DynamoDbFramework::Index
|
3
|
+
|
4
|
+
index_name 'example_index'
|
5
|
+
table ExampleTable
|
6
|
+
partition_key :name, :S
|
7
|
+
range_key :id, :S
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class ExampleIndexWithoutIndexName
|
12
|
+
extend DynamoDbFramework::Index
|
13
|
+
|
14
|
+
table ExampleTable
|
15
|
+
partition_key :name, :S
|
16
|
+
range_key :id, :S
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
class ExampleIndexWithoutTable
|
21
|
+
extend DynamoDbFramework::Index
|
22
|
+
|
23
|
+
index_name 'example_index'
|
24
|
+
partition_key :name, :S
|
25
|
+
range_key :id, :S
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class ExampleIndexWithoutPartitionKey
|
30
|
+
extend DynamoDbFramework::Index
|
31
|
+
|
32
|
+
table ExampleTable
|
33
|
+
index_name 'example_index'
|
34
|
+
range_key :id, :S
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class ExampleIndexWithoutRangeKey
|
39
|
+
extend DynamoDbFramework::Index
|
40
|
+
|
41
|
+
index_name 'example_index'
|
42
|
+
table ExampleTable
|
43
|
+
partition_key :name, :S
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class ExampleTable
|
2
|
+
extend DynamoDbFramework::Table
|
3
|
+
|
4
|
+
table_name 'example'
|
5
|
+
partition_key :id, :S
|
6
|
+
range_key :timestamp, :N
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class ExampleTable2
|
11
|
+
extend DynamoDbFramework::Table
|
12
|
+
|
13
|
+
table_name 'example2'
|
14
|
+
partition_key :name, :S
|
15
|
+
range_key :id, :S
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class ExampleTableWithoutTableName
|
20
|
+
extend DynamoDbFramework::Table
|
21
|
+
|
22
|
+
partition_key :id, :S
|
23
|
+
range_key :timestamp, :N
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class ExampleTableWithoutPartitionKey
|
28
|
+
extend DynamoDbFramework::Table
|
29
|
+
|
30
|
+
table_name 'example'
|
31
|
+
range_key :timestamp, :N
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class ExampleTableWithoutRangeKey
|
36
|
+
extend DynamoDbFramework::Table
|
37
|
+
|
38
|
+
table_name 'example'
|
39
|
+
partition_key :id, :S
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe DynamoDbFramework::HashHelper do
|
4
|
+
describe '#to_hash' do
|
5
|
+
let(:service_klass) do
|
6
|
+
Class.new do
|
7
|
+
attr_accessor :code, :created
|
8
|
+
end
|
9
|
+
end
|
10
|
+
let(:account_klass) do
|
11
|
+
Class.new do
|
12
|
+
attr_accessor :name, :email, :services
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:current_time ) { Time.now }
|
17
|
+
let(:name ) { 'Service User' }
|
18
|
+
let(:email ) { 'service.user@sage,com' }
|
19
|
+
let(:hr_service_code ) { 'HR' }
|
20
|
+
let(:hr_service_created ) { current_time.to_i }
|
21
|
+
let(:crm_service_code ) { 'CRM' }
|
22
|
+
let(:crm_service_created) { current_time.to_i }
|
23
|
+
|
24
|
+
let(:hr_service) do
|
25
|
+
service_klass.new.tap do |obj|
|
26
|
+
obj.code = hr_service_code
|
27
|
+
obj.created = hr_service_created
|
28
|
+
end
|
29
|
+
end
|
30
|
+
let(:crm_service) do
|
31
|
+
service_klass.new.tap do |obj|
|
32
|
+
obj.code = crm_service_code
|
33
|
+
obj.created = crm_service_created
|
34
|
+
end
|
35
|
+
end
|
36
|
+
let(:account) do
|
37
|
+
account_klass.new.tap do |obj|
|
38
|
+
obj.name = name
|
39
|
+
obj.email = email
|
40
|
+
obj.services = [hr_service, crm_service]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when given a hash' do
|
45
|
+
let(:obj) do
|
46
|
+
{
|
47
|
+
name: name,
|
48
|
+
email: email
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
it { expect(subject.to_hash(obj)).to eq(obj) }
|
53
|
+
|
54
|
+
context 'and one item in the hash is nil' do
|
55
|
+
let(:name) { nil }
|
56
|
+
let(:expected) { { email: email } }
|
57
|
+
|
58
|
+
it { expect(subject.to_hash(obj)).to eq(expected) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when all attributes are present' do
|
63
|
+
let(:expected) do
|
64
|
+
{
|
65
|
+
name: "Service User",
|
66
|
+
email: "service.user@sage,com",
|
67
|
+
services: [
|
68
|
+
{ code: hr_service_code , created: hr_service_created },
|
69
|
+
{ code: crm_service_code, created: crm_service_created}
|
70
|
+
]
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
it { expect(subject.to_hash(account)).to eq(expected) }
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when parent obj has nil attribute' do
|
78
|
+
let(:name) { nil }
|
79
|
+
let(:expected) do
|
80
|
+
{
|
81
|
+
email: "service.user@sage,com",
|
82
|
+
services: [
|
83
|
+
{ code: hr_service_code , created: hr_service_created },
|
84
|
+
{ code: crm_service_code, created: crm_service_created}
|
85
|
+
]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
it { expect(subject.to_hash(account)).to eq(expected) }
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when child objects have nil attributes' do
|
93
|
+
let(:hr_service_code ) { nil }
|
94
|
+
let(:crm_service_created) { nil }
|
95
|
+
|
96
|
+
let(:expected) do
|
97
|
+
{
|
98
|
+
name: name,
|
99
|
+
email: email,
|
100
|
+
services: [
|
101
|
+
{ created: hr_service_created },
|
102
|
+
{ code: crm_service_code}
|
103
|
+
]
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
it { expect(subject.to_hash(account)).to eq(expected) }
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when parent and child objects have nil attributes' do
|
111
|
+
let(:email ) { nil }
|
112
|
+
let(:hr_service_code ) { nil }
|
113
|
+
let(:crm_service_created) { nil }
|
114
|
+
|
115
|
+
let(:expected) do
|
116
|
+
{
|
117
|
+
name: name,
|
118
|
+
services: [
|
119
|
+
{ created: hr_service_created },
|
120
|
+
{ code: crm_service_code}
|
121
|
+
]
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
it { expect(subject.to_hash(account)).to eq(expected) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
it { expect({a: 1, b: nil}.reject{|k, v| v.nil?}).to eq({a: 1}) }
|
129
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
require 'aws-sdk-core'
|
4
|
+
require 'dynamodb_framework'
|
5
|
+
require_relative '../spec/test_migration_script1'
|
6
|
+
require_relative '../spec/test_migration_script2'
|
7
|
+
require_relative '../spec/test_item.rb'
|
8
|
+
require_relative '../spec/example_table'
|
9
|
+
require_relative '../spec/example_index'
|
10
|
+
require 'pry'
|
11
|
+
|
12
|
+
require 'simplecov'
|
13
|
+
SimpleCov.start do
|
14
|
+
add_filter '/spec/'
|
15
|
+
end
|
16
|
+
|
17
|
+
DYNAMODB_STORE_ENDPOINT = 'http://dynamodb:8000'
|
18
|
+
|
19
|
+
Aws.config[:credentials] = Aws::Credentials.new('test_key', 'test_secret')
|
20
|
+
Aws.config[:region] = 'eu-west-1'
|
21
|
+
|
22
|
+
RSpec.configure do |config|
|
23
|
+
config.expect_with :rspec do |expectations|
|
24
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
25
|
+
end
|
26
|
+
|
27
|
+
config.mock_with :rspec do |mocks|
|
28
|
+
mocks.verify_partial_doubles = true
|
29
|
+
end
|
30
|
+
|
31
|
+
config.order = :defined
|
32
|
+
end
|
33
|
+
|
34
|
+
DynamoDbFramework.logger.level = Logger::ERROR
|
data/spec/test_item.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
class TestMigrationScript1 < DynamoDbFramework::MigrationScript
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@timestamp = '20160318110710'
|
5
|
+
@namespace = 'test_namespace'
|
6
|
+
@store = DynamoDbFramework::Store.new({ endpoint: DYNAMODB_STORE_ENDPOINT, aws_region: 'eu-west-1' })
|
7
|
+
@table_manager = DynamoDbFramework::TableManager.new(@store)
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply
|
11
|
+
|
12
|
+
builder = DynamoDbFramework::AttributesBuilder.new
|
13
|
+
builder.add({ name: :id, type: :string, key: :hash })
|
14
|
+
@table_manager.create_table({ name: 'test1', attributes: builder.attributes })
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def undo
|
19
|
+
|
20
|
+
@table_manager.drop('test1')
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class TestMigrationScript2 < DynamoDbFramework::MigrationScript
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
@timestamp = '20160318110730'
|
5
|
+
@namespace = 'test_namespace'
|
6
|
+
@store = DynamoDbFramework::Store.new({ endpoint: DYNAMODB_STORE_ENDPOINT, aws_region: 'eu-west-1' })
|
7
|
+
@table_manager = DynamoDbFramework::TableManager.new(@store)
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply
|
11
|
+
|
12
|
+
builder = DynamoDbFramework::AttributesBuilder.new
|
13
|
+
builder.add(:id, :S)
|
14
|
+
@table_manager.create('test2', builder.attributes, :id)
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def undo
|
19
|
+
|
20
|
+
@table_manager.drop('test2')
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|