dynamo-record 1.4.1 → 1.4.2
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/dynamo/record/version.rb +1 -1
- data/{Gemfile → spec/gemfiles/rails-5.2.gemfile} +3 -1
- data/spec/gemfiles/rails-6.0.gemfile +7 -0
- data/spec/gemfiles/rails-6.1.gemfile +7 -0
- data/spec/internal/app/models/model1.rb +9 -0
- data/spec/internal/app/models/model2.rb +9 -0
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/db/dynamo_migrate/20170402163638_create_model_1.rb +13 -0
- data/spec/internal/db/dynamo_migrate/20170402163639_create_model_2.rb +9 -0
- data/spec/internal/db/invalid_dynamo_migrate/add_model_1_stream.rb +9 -0
- data/spec/lib/dynamo/record/batch_get_spec.rb +85 -0
- data/spec/lib/dynamo/record/batch_write_spec.rb +90 -0
- data/spec/lib/dynamo/record/model_existence_vaildator_spec.rb +43 -0
- data/spec/lib/dynamo/record/model_spec.rb +120 -0
- data/spec/lib/dynamo/record/table_migration_spec.rb +68 -0
- data/spec/lib/dynamo/record/task_helpers/cleanup_spec.rb +28 -0
- data/spec/lib/dynamo/record/task_helpers/drop_all_tables_spec.rb +21 -0
- data/spec/lib/dynamo/record/task_helpers/drop_table_spec.rb +27 -0
- data/spec/lib/dynamo/record/task_helpers/list_tables_spec.rb +12 -0
- data/spec/lib/dynamo/record/task_helpers/migration_runner_spec.rb +54 -0
- data/spec/lib/dynamo/record/task_helpers/scale_spec.rb +64 -0
- data/spec/lib/dynamo/record/version_spec.rb +7 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/support/shared_contexts/with_dummy_model.rb +28 -0
- metadata +52 -30
- data/.dockerignore +0 -17
- data/.gitignore +0 -16
- data/.rspec +0 -2
- data/.rubocop.yml +0 -61
- data/.travis.yml +0 -28
- data/Dockerfile +0 -24
- data/Jenkinsfile +0 -45
- data/LICENSE.txt +0 -21
- data/docker-compose.override.example.yml +0 -19
- data/docker-compose.yml +0 -20
- data/dynamo-record.gemspec +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 193614a73977889190f759bb367f89d72ea36965a093488dc2203be83f28e37e
|
4
|
+
data.tar.gz: cc792d8f2d367b0fd14b7af1578d6f0a3f249b589fd8e7e880d9f40e9715b383
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d2e98eeafd27be2c4e00e861515cd03c222de15a8eff4196377113d7791fa311b873b389037c8834cd039ab3df3590ff1e6bbdcf9c99ba09f8f567c8040acc4
|
7
|
+
data.tar.gz: 8250e400b5c6a63dd0d33daedf9f5d1bba2ba8f5fdba1326543075dbc2c90a91bf4c7530a1edea2b4112ee2c8f56bba8c33e115efdc6d94177c11833a784245b
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DynamoMigrate
|
4
|
+
class CreateModel1 < Dynamo::Record::TableMigration
|
5
|
+
def self.table_config
|
6
|
+
Aws::Record::TableConfig.define do |config|
|
7
|
+
config.model_class Model1
|
8
|
+
config.read_capacity_units 1
|
9
|
+
config.write_capacity_units 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::BatchGet do
|
4
|
+
include_context 'with dummy model'
|
5
|
+
|
6
|
+
table_name = DynamoDummyModel.table_name
|
7
|
+
|
8
|
+
let(:records) { (1..5).map { |id| DynamoDummyModel.new(id: id, name: (64 + id).chr) } }
|
9
|
+
let(:keys) { (1..5).map { |id| { id: id } } }
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
DynamoDummyModel.batch_save!(records)
|
13
|
+
expect(DynamoDummyModel.scan.count).to eq 5
|
14
|
+
end
|
15
|
+
|
16
|
+
# Overrides sleep everywhere to speed up specs.
|
17
|
+
before { allow_any_instance_of(Object).to receive(:sleep) }
|
18
|
+
|
19
|
+
it 'gets all at once' do
|
20
|
+
result = DynamoDummyModel.batch_get(keys)
|
21
|
+
expect(result.map(&:name).sort).to eq %w[A B C D E]
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles empty array' do
|
25
|
+
result = DynamoDummyModel.batch_get([])
|
26
|
+
expect(result.count).to eq 0
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'gets all items if item queue is larger than possible in one batch get' do
|
30
|
+
stub_const('Dynamo::Record::BatchGet::BATCH_SIZE', 3)
|
31
|
+
result = DynamoDummyModel.batch_get(keys)
|
32
|
+
expect(result.map(&:name).sort).to eq %w[A B C D E]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'raises when attempts are unsuccessful' do
|
36
|
+
allow(DynamoDummyModel).to receive(:max_retries).and_return(1)
|
37
|
+
dynamodb_client_stub = double
|
38
|
+
allow(DynamoDummyModel).to receive(:dynamodb_client).and_return(dynamodb_client_stub)
|
39
|
+
expect(dynamodb_client_stub).to receive(:batch_get_item) do |opts|
|
40
|
+
items = opts[:request_items][table_name][:keys]
|
41
|
+
Aws::DynamoDB::Types::BatchGetItemOutput.new(
|
42
|
+
responses: { table_name => [] },
|
43
|
+
unprocessed_keys: { table_name => Aws::DynamoDB::Types::KeysAndAttributes.new(keys: items) }
|
44
|
+
)
|
45
|
+
end
|
46
|
+
expect do
|
47
|
+
DynamoDummyModel.batch_get(keys)
|
48
|
+
end.to raise_error(Dynamo::Record::BatchGet::NumberOfRetriesExceeded)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'reprocesses unprocessed items' do
|
52
|
+
dynamodb_client_stub = double
|
53
|
+
original_batch_get_item = DynamoDummyModel.dynamodb_client.method(:batch_get_item)
|
54
|
+
allow(DynamoDummyModel).to receive(:dynamodb_client).and_return(dynamodb_client_stub)
|
55
|
+
expect(dynamodb_client_stub).to receive(:batch_get_item) do |opts|
|
56
|
+
# Pick two successful items, return the remaining keys
|
57
|
+
items = opts[:request_items][table_name][:keys]
|
58
|
+
success = items.sample(2)
|
59
|
+
response = original_batch_get_item.call(opts)
|
60
|
+
pr, = response.responses[table_name].partition { |r| success.map { |i| i['id'] }.include? r['id'] }
|
61
|
+
unpr = items - success
|
62
|
+
Aws::DynamoDB::Types::BatchGetItemOutput.new(
|
63
|
+
responses: { table_name => pr },
|
64
|
+
unprocessed_keys: { table_name => Aws::DynamoDB::Types::KeysAndAttributes.new(keys: unpr) }
|
65
|
+
)
|
66
|
+
end.exactly(3).times
|
67
|
+
result = DynamoDummyModel.batch_get(keys)
|
68
|
+
expect(result.map(&:name).sort).to eq %w[A B C D E]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'retries if provisioned throughput is exceeded' do
|
72
|
+
stub_const('Dynamo::Record::BatchGet::BATCH_SIZE', 3)
|
73
|
+
call_count = 0
|
74
|
+
original_batch_get_item = DynamoDummyModel.dynamodb_client.method(:batch_get_item)
|
75
|
+
expect(DynamoDummyModel.dynamodb_client).to receive(:batch_get_item) do |opts|
|
76
|
+
call_count += 1
|
77
|
+
raise Aws::DynamoDB::Errors::ProvisionedThroughputExceededException.new(nil, nil) if call_count == 1
|
78
|
+
|
79
|
+
original_batch_get_item.call(opts)
|
80
|
+
end.exactly(3).times
|
81
|
+
|
82
|
+
result = DynamoDummyModel.batch_get(keys)
|
83
|
+
expect(result.map(&:name).sort).to eq %w[A B C D E]
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::BatchWrite do
|
4
|
+
include_context 'with dummy model'
|
5
|
+
|
6
|
+
shared_examples_for 'a batch write' do |method|
|
7
|
+
# Overrides sleep everywhere to speed up specs.
|
8
|
+
before { allow_any_instance_of(Object).to receive(:sleep) }
|
9
|
+
|
10
|
+
it 'writes all at once' do
|
11
|
+
DynamoDummyModel.send(method, records)
|
12
|
+
expect(DynamoDummyModel.scan.count).to eq expected_count
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'handles empty array' do
|
16
|
+
DynamoDummyModel.send(method, [])
|
17
|
+
expect(DynamoDummyModel.scan.count).to eq num_records
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'writes all items if item queue is larger than possible in one batch write' do
|
21
|
+
stub_const('Dynamo::Record::BatchWrite::BATCH_SIZE', 3)
|
22
|
+
DynamoDummyModel.send(method, records)
|
23
|
+
expect(DynamoDummyModel.scan.count).to eq expected_count
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'writes all items if payload size would be exceeded in a single write' do
|
27
|
+
stub_const('Dynamo::Record::BatchWrite::MAX_PAYLOAD_SIZE', 50)
|
28
|
+
records = (1..5).map { |id| DynamoDummyModel.new(id: id) }
|
29
|
+
DynamoDummyModel.send(method, records)
|
30
|
+
expect(DynamoDummyModel.scan.count).to eq expected_count
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'reprocesses unprocessed items' do
|
34
|
+
records = [DynamoDummyModel.new(id: 1)]
|
35
|
+
dynamodb_client_stub = double
|
36
|
+
expect(DynamoDummyModel).to receive(:max_retries).and_return(1).exactly(2).times
|
37
|
+
expect(DynamoDummyModel).to receive(:dynamodb_client).and_return(dynamodb_client_stub).exactly(2).times
|
38
|
+
allow(dynamodb_client_stub).to receive(:batch_write_item) do |opts|
|
39
|
+
items = opts[:request_items][DynamoDummyModel.table_name].map { |ri| Aws::DynamoDB::Types::WriteRequest.new ri }
|
40
|
+
double(unprocessed_items: { DynamoDummyModel.table_name => items })
|
41
|
+
end
|
42
|
+
expect do
|
43
|
+
DynamoDummyModel.send(method, records)
|
44
|
+
end.to raise_error(Dynamo::Record::BatchWrite::NumberOfRetriesExceeded)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'fails if a record is too large' do
|
48
|
+
stub_const('Dynamo::Record::BatchRequest::MAX_RECORD_SIZE', 5)
|
49
|
+
expect do
|
50
|
+
DynamoDummyModel.send(method, records)
|
51
|
+
end.to raise_error(Dynamo::Record::RecordTooLargeError)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'retries if provisioned throughput is exceeded' do
|
55
|
+
stub_const('Dynamo::Record::BatchWrite::BATCH_SIZE', 3)
|
56
|
+
call_count = 0
|
57
|
+
original_batch_write_item = DynamoDummyModel.dynamodb_client.method(:batch_write_item)
|
58
|
+
expect(DynamoDummyModel.dynamodb_client).to receive(:batch_write_item) do |opts|
|
59
|
+
call_count += 1
|
60
|
+
raise Aws::DynamoDB::Errors::ProvisionedThroughputExceededException.new(nil, nil) if call_count == 1
|
61
|
+
|
62
|
+
original_batch_write_item.call(opts)
|
63
|
+
end.exactly(3).times
|
64
|
+
|
65
|
+
DynamoDummyModel.send(method, records)
|
66
|
+
expect(DynamoDummyModel.scan.count).to eq expected_count
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '.batch_save!' do
|
71
|
+
let(:records) { (1..5).map { |id| DynamoDummyModel.new(id: id) } }
|
72
|
+
let(:expected_count) { 5 }
|
73
|
+
let(:num_records) { 0 }
|
74
|
+
|
75
|
+
it_behaves_like 'a batch write', :batch_save!
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '.batch_delete!' do
|
79
|
+
let(:records) { (1..5).map { |id| DynamoDummyModel.new(id: id) } }
|
80
|
+
let(:expected_count) { 0 }
|
81
|
+
let(:num_records) { 5 }
|
82
|
+
|
83
|
+
before(:each) do
|
84
|
+
DynamoDummyModel.batch_save!(records)
|
85
|
+
expect(DynamoDummyModel.scan.count).to eq 5
|
86
|
+
end
|
87
|
+
|
88
|
+
it_behaves_like 'a batch write', :batch_delete!
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::ModelExistenceValidator do
|
4
|
+
class TestModel
|
5
|
+
def self.exists?(id)
|
6
|
+
id == 1
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#validate_each' do
|
11
|
+
before(:each) do
|
12
|
+
validator.validate_each(record, :test_model_id, value)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:record) do
|
16
|
+
Class.new do
|
17
|
+
attr_accessor :errors
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@errors = { test_model_id: [] }
|
21
|
+
end
|
22
|
+
end.new
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:validator) { described_class.new(attributes: [:test_model_id], model: TestModel) }
|
26
|
+
|
27
|
+
context 'valid record' do
|
28
|
+
let(:value) { 1 }
|
29
|
+
|
30
|
+
it 'has no errors' do
|
31
|
+
expect(record.errors[:test_model_id]).to be_empty
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'invalid record' do
|
36
|
+
let(:value) { 2 }
|
37
|
+
|
38
|
+
it 'has errors' do
|
39
|
+
expect(record.errors[:test_model_id]).not_to be_empty
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::Model do
|
4
|
+
class TestDynamoModel
|
5
|
+
include Dynamo::Record::Model
|
6
|
+
|
7
|
+
integer_attr :test_id_1, hash_key: true
|
8
|
+
integer_attr :test_id_2, range_key: true
|
9
|
+
integer_attr :test_id_3
|
10
|
+
composite_integer_attr(
|
11
|
+
:test_id_4,
|
12
|
+
parts: %i[test_id_4_1 test_id_4_2]
|
13
|
+
)
|
14
|
+
|
15
|
+
global_secondary_index(
|
16
|
+
:gsi_1,
|
17
|
+
hash_key: :test_id_3,
|
18
|
+
range_key: :test_id_2,
|
19
|
+
projection: {
|
20
|
+
projection_type: 'ALL'
|
21
|
+
}
|
22
|
+
)
|
23
|
+
|
24
|
+
local_secondary_index(
|
25
|
+
:lsi_1,
|
26
|
+
range_key: :test_id_3,
|
27
|
+
projection: {
|
28
|
+
projection_type: 'ALL'
|
29
|
+
}
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '.table_name' do
|
34
|
+
it 'should have correct table name' do
|
35
|
+
expect(TestDynamoModel.table_name).to eq 'test-test_dynamo_models'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'composite keys' do
|
40
|
+
before do
|
41
|
+
@d_1 = 'data_1'
|
42
|
+
@d_2 = 'data_2'
|
43
|
+
@key = TestDynamoModel.composite_key(@d_1, @d_2)
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '.split_composite' do
|
47
|
+
it 'returns the input from composite_key' do
|
48
|
+
r_1, r_2 = TestDynamoModel.split_composite(@key)
|
49
|
+
expect(r_1).to eq @d_1
|
50
|
+
expect(r_2).to eq @d_2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.composite_key' do
|
55
|
+
it 'returns the composite key' do
|
56
|
+
expect(@key).to eq "#{@d_1}|#{@d_2}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '.find' do
|
62
|
+
it 'raises Aws::Record::Errors::NotFound if no record matches' do
|
63
|
+
resp = Aws::DynamoDB::Types::GetItemOutput.new
|
64
|
+
expect(TestDynamoModel.dynamodb_client).to receive(:get_item).once.and_return(resp)
|
65
|
+
expect { TestDynamoModel.find(test_id_1: 'omg', test_id_2: 'pls') }.to raise_error(Aws::Record::Errors::NotFound)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'find queries' do
|
70
|
+
before do
|
71
|
+
expect(TestDynamoModel).to receive(:query).once
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.find_all_by_hash_key' do
|
75
|
+
it 'queries by hash key' do
|
76
|
+
TestDynamoModel.find_all_by_hash_key(1)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '.find_all_by_gsi_hash_key' do
|
81
|
+
it 'queries the gsi' do
|
82
|
+
TestDynamoModel.find_all_by_gsi_hash_key('gsi_1', 1)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '.find_all_by_gsi_hash_and_range_keys' do
|
87
|
+
it 'queries the gsi' do
|
88
|
+
TestDynamoModel.find_all_by_gsi_hash_and_range_keys('gsi_1', 1, 5)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '.find_all_by_lsi_hash_key' do
|
93
|
+
it 'queries the lsi' do
|
94
|
+
TestDynamoModel.find_all_by_lsi_hash_key('lsi_1', 1)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe '.find_all_by_lsi_hash_and_range_keys' do
|
99
|
+
it 'queries the lsi' do
|
100
|
+
TestDynamoModel.find_all_by_lsi_hash_and_range_keys('lsi_1', 1, 3)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '.find_all_by_index_hash_and_range_keys' do
|
105
|
+
it 'can fetch item ranges' do
|
106
|
+
hash = { name: 'test_id_1', value: 2 }
|
107
|
+
range = { expression: 'test_id_2 > :rkv', value: 10 }
|
108
|
+
TestDynamoModel.find_all_by_index_hash_and_range_keys(hash_config: hash, range_config: range)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#read_attribute_for_serialization' do
|
114
|
+
it 'generates an attribute hash' do
|
115
|
+
key = TestDynamoModel.composite_key(4, 5)
|
116
|
+
m = TestDynamoModel.new(test_id_1: 1, test_id_2: 2, test_id_3: 3, test_id_4: key)
|
117
|
+
expect(m.attribute_hash).to eq(test_id_1: 1, test_id_2: 2, test_id_3: 3, test_id_4: key)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TableMigration do
|
4
|
+
class TestDynamoTableMigration
|
5
|
+
include Aws::Record
|
6
|
+
|
7
|
+
integer_attr :test_id_1, hash_key: true
|
8
|
+
integer_attr :test_id_2, range_key: true
|
9
|
+
end
|
10
|
+
|
11
|
+
class CreateTestDynamoTableMigration < Dynamo::Record::TableMigration
|
12
|
+
def self.up
|
13
|
+
migrate(TestDynamoTableMigration) do |migration|
|
14
|
+
migration.create!(
|
15
|
+
provisioned_throughput: {
|
16
|
+
read_capacity_units: 1,
|
17
|
+
write_capacity_units: 1
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:client) { double(describe_table: true) }
|
25
|
+
|
26
|
+
describe '.migrate' do
|
27
|
+
it 'returns `:migrated` if successful' do
|
28
|
+
expect_any_instance_of(Aws::Record::TableMigration).to receive(:client).and_return(client)
|
29
|
+
expect(client).to receive(:describe_table).and_raise(
|
30
|
+
Aws::DynamoDB::Errors::ResourceNotFoundException.new(nil, '')
|
31
|
+
)
|
32
|
+
|
33
|
+
expect_any_instance_of(Aws::Record::TableMigration).to receive(:create!).once
|
34
|
+
expect_any_instance_of(Aws::Record::TableMigration).to receive(:wait_until_available).once
|
35
|
+
|
36
|
+
expect(CreateTestDynamoTableMigration.up).to eq :migrated
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'returns `:exists` if table already exists' do
|
40
|
+
expect_any_instance_of(Aws::Record::TableMigration).to receive(:client).and_return(client)
|
41
|
+
|
42
|
+
expect(CreateTestDynamoTableMigration.up).to eq :exists
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '.add_stream' do
|
47
|
+
it 'raises an error if ValidationException raised' do
|
48
|
+
allow_any_instance_of(Aws::Record::TableMigration).to(
|
49
|
+
receive(:update!).and_raise(Aws::DynamoDB::Errors::ValidationException.new(nil, nil))
|
50
|
+
)
|
51
|
+
expect { CreateTestDynamoTableMigration.add_stream(TestDynamoTableMigration) }.to(
|
52
|
+
raise_error Aws::DynamoDB::Errors::ValidationException
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'does not raise an excetion if stream already enabled' do
|
57
|
+
message = 'Table already has an enabled stream'
|
58
|
+
expect_any_instance_of(Aws::DynamoDB::Errors::ValidationException).to(
|
59
|
+
receive(:message).at_least(:once).and_return(message)
|
60
|
+
)
|
61
|
+
allow_any_instance_of(Aws::Record::TableMigration).to(
|
62
|
+
receive(:update!).and_raise(Aws::DynamoDB::Errors::ValidationException.new(nil, ''))
|
63
|
+
)
|
64
|
+
expect { CreateTestDynamoTableMigration.add_stream(TestDynamoTableMigration) }.to_not raise_error
|
65
|
+
expect(CreateTestDynamoTableMigration.add_stream(TestDynamoTableMigration)).to eq message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TaskHelpers::Cleanup do
|
4
|
+
describe '.run' do
|
5
|
+
it 'raises error if rails is in production' do
|
6
|
+
allow(Rails).to receive(:env).and_return(
|
7
|
+
ActiveSupport::StringInquirer.new('production')
|
8
|
+
)
|
9
|
+
expect do
|
10
|
+
Dynamo::Record::TaskHelpers::Cleanup.run
|
11
|
+
end.to raise_error('Task not available on production')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'deletes records in all DynamoDB tables' do
|
15
|
+
Dir[Rails.root.join('app/models/*.rb').to_s].each do |filename|
|
16
|
+
klass = File.basename(filename, '.rb').camelize.constantize
|
17
|
+
next unless klass.included_modules.include? Dynamo::Record::Model
|
18
|
+
|
19
|
+
object = double.as_null_object
|
20
|
+
list = [object]
|
21
|
+
expect(klass).to receive(:scan).and_return(list)
|
22
|
+
expect(object).to receive(:delete!)
|
23
|
+
end
|
24
|
+
|
25
|
+
described_class.run
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TaskHelpers::DropAllTables do
|
4
|
+
describe '.run' do
|
5
|
+
it 'raises error if rails is in production' do
|
6
|
+
allow(Rails).to receive(:env).and_return(
|
7
|
+
ActiveSupport::StringInquirer.new('production')
|
8
|
+
)
|
9
|
+
expect do
|
10
|
+
Dynamo::Record::TaskHelpers::DropAllTables.run
|
11
|
+
end.to raise_error('Task not available on production')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'drops all dynamo tables' do
|
15
|
+
expect_any_instance_of(Aws::DynamoDB::Client).to receive(:list_tables)
|
16
|
+
.and_return double(table_names: %w[test_t1 test_t2 test_t3])
|
17
|
+
expect_any_instance_of(Aws::DynamoDB::Client).to receive(:delete_table).at_least(3).times
|
18
|
+
Dynamo::Record::TaskHelpers::DropAllTables.run
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TaskHelpers::DropTable do
|
4
|
+
include_context 'with dummy model'
|
5
|
+
|
6
|
+
class BadDummyModel; end # rubocop:disable Lint/EmptyClass
|
7
|
+
|
8
|
+
describe '#run' do
|
9
|
+
it 'raises error if specified class does not include Aws::Record' do
|
10
|
+
expect do
|
11
|
+
Dynamo::Record::TaskHelpers::DropTable.run('BadDummyModel')
|
12
|
+
end.to raise_error('Cannot do operations on a non-existent table')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'drops dynamo table' do
|
16
|
+
dynamodb = Aws::DynamoDB::Client.new
|
17
|
+
table = dynamodb.describe_table(table_name: DynamoDummyModel.table_name)
|
18
|
+
expect(table).not_to be_empty
|
19
|
+
|
20
|
+
Dynamo::Record::TaskHelpers::DropTable.run DynamoDummyModel.table_name
|
21
|
+
|
22
|
+
expect do
|
23
|
+
dynamodb.describe_table(table_name: DynamoDummyModel.table_name)
|
24
|
+
end.to raise_error(Aws::DynamoDB::Errors::ResourceNotFoundException)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TaskHelpers::ListTables do
|
4
|
+
describe '#run' do
|
5
|
+
include_context 'with dummy model'
|
6
|
+
it 'lists all dynamo tables' do
|
7
|
+
list = Dynamo::Record::TaskHelpers::ListTables.run
|
8
|
+
|
9
|
+
expect(list).to include DynamoDummyModel.table_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TaskHelpers::MigrationRunner do
|
4
|
+
describe '.run' do
|
5
|
+
it 'runs migration for each dynamo table' do
|
6
|
+
allow_any_instance_of(Aws::Record::TableMigration).to(
|
7
|
+
receive(:client).and_raise(
|
8
|
+
Aws::DynamoDB::Errors::ResourceNotFoundException.new(nil, '')
|
9
|
+
)
|
10
|
+
)
|
11
|
+
|
12
|
+
allow_any_instance_of(Aws::Record::TableMigration).to receive(:create!)
|
13
|
+
allow_any_instance_of(Aws::Record::TableConfig).to receive(:migrate!)
|
14
|
+
allow_any_instance_of(Aws::Record::TableMigration).to receive(:update!)
|
15
|
+
allow_any_instance_of(Aws::Record::TableMigration).to receive(:wait_until_available)
|
16
|
+
messages = []
|
17
|
+
|
18
|
+
Dynamo::Record::TaskHelpers::MigrationRunner.run do |thing|
|
19
|
+
messages << thing
|
20
|
+
end
|
21
|
+
|
22
|
+
switch = true
|
23
|
+
messages.each do |message|
|
24
|
+
expect(message).to include(switch ? 'Migrating:' : 'Migration successful')
|
25
|
+
switch = !switch
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'displays failed migrations' do
|
30
|
+
allow_any_instance_of(Aws::Record::TableConfig).to(
|
31
|
+
receive(:migrate!).and_raise(
|
32
|
+
StandardError.new('')
|
33
|
+
)
|
34
|
+
)
|
35
|
+
messages = []
|
36
|
+
|
37
|
+
Dynamo::Record::TaskHelpers::MigrationRunner.run do |thing|
|
38
|
+
messages << thing
|
39
|
+
end
|
40
|
+
|
41
|
+
switch = true
|
42
|
+
messages.each do |message|
|
43
|
+
expect(message).to include(switch ? 'Migrating:' : 'Migration failed')
|
44
|
+
switch = !switch
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'raises on non-numeric prefixes' do
|
49
|
+
expect do
|
50
|
+
Dynamo::Record::TaskHelpers::MigrationRunner.run('db/invalid_dynamo_migrate')
|
51
|
+
end.to raise_error(/Non-numeric prefix/)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Dynamo::Record::TaskHelpers::Scale do
|
4
|
+
include_context 'with dummy model'
|
5
|
+
|
6
|
+
describe '.run' do
|
7
|
+
it 'changes both throughputs on the table' do
|
8
|
+
migration = Aws::Record::TableMigration.new(DynamoDummyModel)
|
9
|
+
current_settings = migration.client.describe_table(table_name: DynamoDummyModel.table_name).table
|
10
|
+
|
11
|
+
# configure unique throughput settings so ordering doesn't matter
|
12
|
+
new_throughput = current_settings.provisioned_throughput.read_capacity_units == 50 ? 49 : 50
|
13
|
+
|
14
|
+
# Run the scaling
|
15
|
+
scale = Dynamo::Record::TaskHelpers::Scale.new 'DynamoDummyModel', 'both', new_throughput
|
16
|
+
scale.run
|
17
|
+
throughput = migration.client.describe_table(table_name: DynamoDummyModel.table_name).table.provisioned_throughput
|
18
|
+
expect(throughput.read_capacity_units).to eq(new_throughput)
|
19
|
+
expect(throughput.write_capacity_units).to eq(new_throughput)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'changes the write throughput on the table' do
|
23
|
+
migration = Aws::Record::TableMigration.new(DynamoDummyModel)
|
24
|
+
current_settings = migration.client.describe_table(table_name: DynamoDummyModel.table_name).table
|
25
|
+
|
26
|
+
# configure unique throughput settings so ordering doesn't matter
|
27
|
+
new_write_throughput = current_settings.provisioned_throughput.write_capacity_units == 20 ? 19 : 20
|
28
|
+
|
29
|
+
# Run the scaling
|
30
|
+
scale = Dynamo::Record::TaskHelpers::Scale.new 'DynamoDummyModel', 'write', new_write_throughput
|
31
|
+
scale.run
|
32
|
+
|
33
|
+
throughput = migration.client.describe_table(table_name: DynamoDummyModel.table_name).table.provisioned_throughput
|
34
|
+
expect(throughput.write_capacity_units).to eq(new_write_throughput)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'changes the read throughput on the table' do
|
38
|
+
migration = Aws::Record::TableMigration.new(DynamoDummyModel)
|
39
|
+
current_settings = migration.client.describe_table(table_name: DynamoDummyModel.table_name).table
|
40
|
+
|
41
|
+
# configure unique throughput settings so ordering doesn't matter
|
42
|
+
new_read_throughput = current_settings.provisioned_throughput.read_capacity_units == 20 ? 19 : 20
|
43
|
+
|
44
|
+
# Run the scaling
|
45
|
+
scale = Dynamo::Record::TaskHelpers::Scale.new 'DynamoDummyModel', 'read', new_read_throughput
|
46
|
+
scale.run
|
47
|
+
|
48
|
+
throughput = migration.client.describe_table(table_name: DynamoDummyModel.table_name).table.provisioned_throughput
|
49
|
+
expect(throughput.read_capacity_units).to eq(new_read_throughput)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'prints description if any param is blank' do
|
53
|
+
message = Dynamo::Record::TaskHelpers::Scale.new('DynamoDummyModel', 'read', nil).run
|
54
|
+
expect(message).to include('Here\'s some usage information:')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'raises exception if attribute selection is invalid' do
|
58
|
+
scale = Dynamo::Record::TaskHelpers::Scale.new('DynamoDummyModel', 'invalid_selection', 20)
|
59
|
+
expect { scale.run }.to raise_error(
|
60
|
+
/provide an appropriate attribute selection/
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Limit coverage reporting to one build:
|
4
|
+
if /^2\.6/ =~ RUBY_VERSION && /6\.0/ =~ ENV['BUNDLE_GEMFILE']
|
5
|
+
require 'simplecov'
|
6
|
+
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter 'lib/dynamo/record/version.rb'
|
9
|
+
add_filter 'spec'
|
10
|
+
track_files 'lib/**/*.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
SimpleCov.minimum_coverage(94)
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'bundler'
|
17
|
+
Bundler.require :default, :development
|
18
|
+
|
19
|
+
Combustion.initialize! do
|
20
|
+
config.dynamo = { 'prefix' => 'test' }
|
21
|
+
end
|
22
|
+
|
23
|
+
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
24
|
+
|
25
|
+
RSpec.configure do |config|
|
26
|
+
# Enable flags like --only-failures and --next-failure
|
27
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
28
|
+
|
29
|
+
config.expect_with :rspec do |c|
|
30
|
+
c.syntax = :expect
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Aws.config.update(
|
35
|
+
dynamodb: { endpoint: ENV['DYNAMO_ENDPOINT'] }
|
36
|
+
)
|
37
|
+
|
38
|
+
WebMock.disable_net_connect!(allow: ['dynamo:8000'])
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_context 'with dummy model' do
|
4
|
+
class DynamoDummyModel
|
5
|
+
include Aws::Record
|
6
|
+
include Dynamo::Record::Model
|
7
|
+
|
8
|
+
integer_attr :id, hash_key: true
|
9
|
+
string_attr :name
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
migration = Aws::Record::TableMigration.new(DynamoDummyModel)
|
14
|
+
migration.create!(
|
15
|
+
provisioned_throughput: {
|
16
|
+
read_capacity_units: 1,
|
17
|
+
write_capacity_units: 1
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
after do
|
23
|
+
dynamodb = Aws::DynamoDB::Client.new
|
24
|
+
dynamodb.delete_table table_name: DynamoDummyModel.table_name
|
25
|
+
rescue Aws::DynamoDB::Errors::ResourceNotFoundException
|
26
|
+
# some tests will drop this table
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,20 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamo-record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
- Ryan Taylor
|
9
|
-
- Bryan Petty
|
10
|
-
- Michael Brewer-Davis
|
11
|
-
- Marc Phillips
|
12
|
-
- Augusto Callejas
|
13
|
-
- Frank Murphy
|
7
|
+
- Alex Slaughter
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
date:
|
11
|
+
date: 2023-08-22 00:00:00.000000000 Z
|
18
12
|
dependencies:
|
19
13
|
- !ruby/object:Gem::Dependency
|
20
14
|
name: activemodel
|
@@ -232,30 +226,12 @@ dependencies:
|
|
232
226
|
version: '1.3'
|
233
227
|
description:
|
234
228
|
email:
|
235
|
-
-
|
236
|
-
- rtaylor@instructure.com
|
237
|
-
- bpetty@instructure.com
|
238
|
-
- mbd@instructure.com
|
239
|
-
- mphillips@instructure.com
|
240
|
-
- acallejas@instructure.com
|
241
|
-
- fmurphy@instructure.com
|
229
|
+
- aslaughter@instructure.com
|
242
230
|
executables: []
|
243
231
|
extensions: []
|
244
232
|
extra_rdoc_files: []
|
245
233
|
files:
|
246
|
-
- ".dockerignore"
|
247
|
-
- ".gitignore"
|
248
|
-
- ".rspec"
|
249
|
-
- ".rubocop.yml"
|
250
|
-
- ".travis.yml"
|
251
|
-
- Dockerfile
|
252
|
-
- Gemfile
|
253
|
-
- Jenkinsfile
|
254
|
-
- LICENSE.txt
|
255
234
|
- README.md
|
256
|
-
- docker-compose.override.example.yml
|
257
|
-
- docker-compose.yml
|
258
|
-
- dynamo-record.gemspec
|
259
235
|
- lib/dynamo/record.rb
|
260
236
|
- lib/dynamo/record/batch_get.rb
|
261
237
|
- lib/dynamo/record/batch_request.rb
|
@@ -273,6 +249,29 @@ files:
|
|
273
249
|
- lib/dynamo/record/task_helpers/scale.rb
|
274
250
|
- lib/dynamo/record/version.rb
|
275
251
|
- lib/tasks/dynamo.rake
|
252
|
+
- spec/gemfiles/rails-5.2.gemfile
|
253
|
+
- spec/gemfiles/rails-6.0.gemfile
|
254
|
+
- spec/gemfiles/rails-6.1.gemfile
|
255
|
+
- spec/internal/app/models/model1.rb
|
256
|
+
- spec/internal/app/models/model2.rb
|
257
|
+
- spec/internal/config/database.yml
|
258
|
+
- spec/internal/db/dynamo_migrate/20170402163638_create_model_1.rb
|
259
|
+
- spec/internal/db/dynamo_migrate/20170402163639_create_model_2.rb
|
260
|
+
- spec/internal/db/invalid_dynamo_migrate/add_model_1_stream.rb
|
261
|
+
- spec/lib/dynamo/record/batch_get_spec.rb
|
262
|
+
- spec/lib/dynamo/record/batch_write_spec.rb
|
263
|
+
- spec/lib/dynamo/record/model_existence_vaildator_spec.rb
|
264
|
+
- spec/lib/dynamo/record/model_spec.rb
|
265
|
+
- spec/lib/dynamo/record/table_migration_spec.rb
|
266
|
+
- spec/lib/dynamo/record/task_helpers/cleanup_spec.rb
|
267
|
+
- spec/lib/dynamo/record/task_helpers/drop_all_tables_spec.rb
|
268
|
+
- spec/lib/dynamo/record/task_helpers/drop_table_spec.rb
|
269
|
+
- spec/lib/dynamo/record/task_helpers/list_tables_spec.rb
|
270
|
+
- spec/lib/dynamo/record/task_helpers/migration_runner_spec.rb
|
271
|
+
- spec/lib/dynamo/record/task_helpers/scale_spec.rb
|
272
|
+
- spec/lib/dynamo/record/version_spec.rb
|
273
|
+
- spec/spec_helper.rb
|
274
|
+
- spec/support/shared_contexts/with_dummy_model.rb
|
276
275
|
homepage: https://github.com/instructure/dynamo-record
|
277
276
|
licenses:
|
278
277
|
- MIT
|
@@ -292,8 +291,31 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
292
291
|
- !ruby/object:Gem::Version
|
293
292
|
version: '0'
|
294
293
|
requirements: []
|
295
|
-
rubygems_version: 3.
|
294
|
+
rubygems_version: 3.4.10
|
296
295
|
signing_key:
|
297
296
|
specification_version: 4
|
298
297
|
summary: Extensions to Aws::Record for working with DynamoDB.
|
299
|
-
test_files:
|
298
|
+
test_files:
|
299
|
+
- spec/gemfiles/rails-5.2.gemfile
|
300
|
+
- spec/gemfiles/rails-6.0.gemfile
|
301
|
+
- spec/gemfiles/rails-6.1.gemfile
|
302
|
+
- spec/internal/app/models/model1.rb
|
303
|
+
- spec/internal/app/models/model2.rb
|
304
|
+
- spec/internal/config/database.yml
|
305
|
+
- spec/internal/db/dynamo_migrate/20170402163638_create_model_1.rb
|
306
|
+
- spec/internal/db/dynamo_migrate/20170402163639_create_model_2.rb
|
307
|
+
- spec/internal/db/invalid_dynamo_migrate/add_model_1_stream.rb
|
308
|
+
- spec/lib/dynamo/record/batch_get_spec.rb
|
309
|
+
- spec/lib/dynamo/record/batch_write_spec.rb
|
310
|
+
- spec/lib/dynamo/record/model_existence_vaildator_spec.rb
|
311
|
+
- spec/lib/dynamo/record/model_spec.rb
|
312
|
+
- spec/lib/dynamo/record/table_migration_spec.rb
|
313
|
+
- spec/lib/dynamo/record/task_helpers/cleanup_spec.rb
|
314
|
+
- spec/lib/dynamo/record/task_helpers/drop_all_tables_spec.rb
|
315
|
+
- spec/lib/dynamo/record/task_helpers/drop_table_spec.rb
|
316
|
+
- spec/lib/dynamo/record/task_helpers/list_tables_spec.rb
|
317
|
+
- spec/lib/dynamo/record/task_helpers/migration_runner_spec.rb
|
318
|
+
- spec/lib/dynamo/record/task_helpers/scale_spec.rb
|
319
|
+
- spec/lib/dynamo/record/version_spec.rb
|
320
|
+
- spec/spec_helper.rb
|
321
|
+
- spec/support/shared_contexts/with_dummy_model.rb
|
data/.dockerignore
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
/.gitignore
|
2
|
-
/.rspec_status
|
3
|
-
/.ruby-version
|
4
|
-
/coverage/
|
5
|
-
/doc/
|
6
|
-
/Dockerfile
|
7
|
-
/docker-compose.yml
|
8
|
-
/docker-compose.override.example.yml
|
9
|
-
/docker-compose.override.yml
|
10
|
-
/dynamo-record-*.gem
|
11
|
-
/Gemfile.lock
|
12
|
-
/log/
|
13
|
-
/pkg/
|
14
|
-
/spec/gemfiles/.bundle/
|
15
|
-
/spec/gemfiles/*.gemfile.lock
|
16
|
-
/switchman-inst-jobs-*.gem
|
17
|
-
/tmp/
|
data/.gitignore
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
/.bundle/
|
2
|
-
/.rspec_status
|
3
|
-
/.ruby-version
|
4
|
-
/.yardoc
|
5
|
-
/_yardoc/
|
6
|
-
/coverage/
|
7
|
-
/docker-compose.override.yml
|
8
|
-
/dynamo-record-*.gem
|
9
|
-
/Gemfile.lock
|
10
|
-
/pkg/
|
11
|
-
/spec/gemfiles/.bundle/
|
12
|
-
/spec/dummy/log/
|
13
|
-
/spec/dummy/tmp/
|
14
|
-
/spec/gemfiles/*.gemfile.lock
|
15
|
-
/spec/reports/
|
16
|
-
/tmp/
|
data/.rspec
DELETED
data/.rubocop.yml
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require:
|
2
|
-
- rubocop-rails
|
3
|
-
|
4
|
-
AllCops:
|
5
|
-
NewCops: enable
|
6
|
-
SuggestExtensions: false
|
7
|
-
TargetRailsVersion: 5.2
|
8
|
-
TargetRubyVersion: 2.6
|
9
|
-
|
10
|
-
Metrics/AbcSize:
|
11
|
-
Max: 20 # Default: 15
|
12
|
-
|
13
|
-
Metrics/ClassLength:
|
14
|
-
Max: 200 # Default: 100
|
15
|
-
|
16
|
-
Metrics/MethodLength:
|
17
|
-
Max: 20 # Default: 10
|
18
|
-
|
19
|
-
Metrics/BlockLength:
|
20
|
-
Max: 30
|
21
|
-
Exclude:
|
22
|
-
- dynamo-record.gemspec
|
23
|
-
- spec/**/*.rb
|
24
|
-
|
25
|
-
Layout/EndAlignment:
|
26
|
-
EnforcedStyleAlignWith: variable
|
27
|
-
|
28
|
-
Layout/LineLength:
|
29
|
-
Max: 120 # Default: 80
|
30
|
-
|
31
|
-
Layout/ParameterAlignment:
|
32
|
-
# Alignment of parameters in multi-line method calls.
|
33
|
-
#
|
34
|
-
# The `with_fixed_indentation` style aligns the following lines with one
|
35
|
-
# level of indentation relative to the start of the line with the method call.
|
36
|
-
#
|
37
|
-
# method_call(a,
|
38
|
-
# b)
|
39
|
-
EnforcedStyle: with_fixed_indentation
|
40
|
-
|
41
|
-
Lint/ConstantDefinitionInBlock:
|
42
|
-
Exclude:
|
43
|
-
- spec/**/*.rb
|
44
|
-
|
45
|
-
Naming/FileName:
|
46
|
-
Exclude:
|
47
|
-
- spec/gemfiles/*
|
48
|
-
|
49
|
-
Naming/VariableNumber:
|
50
|
-
EnforcedStyle: snake_case
|
51
|
-
|
52
|
-
Style/Documentation:
|
53
|
-
# This cop checks for missing top-level documentation of classes and modules.
|
54
|
-
# Classes with no body and namespace modules are exempt from the check.
|
55
|
-
# Namespace modules are modules that have nothing in their bodies except
|
56
|
-
# classes or other modules.
|
57
|
-
Enabled: false
|
58
|
-
|
59
|
-
Style/NumericPredicate:
|
60
|
-
Exclude:
|
61
|
-
- spec/**/*.rb
|
data/.travis.yml
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
dist: trusty
|
2
|
-
sudo: false
|
3
|
-
language: ruby
|
4
|
-
cache: bundler
|
5
|
-
|
6
|
-
rvm:
|
7
|
-
- 2.6
|
8
|
-
- 2.7
|
9
|
-
- 3.0
|
10
|
-
|
11
|
-
gemfile:
|
12
|
-
- spec/gemfiles/rails-5.2.gemfile
|
13
|
-
- spec/gemfiles/rails-6.0.gemfile
|
14
|
-
- spec/gemfiles/rails-6.1.gemfile
|
15
|
-
|
16
|
-
# Rails 5.2 doesn't support Ruby 3.0, so don't try
|
17
|
-
matrix:
|
18
|
-
exclude:
|
19
|
-
- rvm: 3.0
|
20
|
-
gemfile: spec/gemfiles/rails-5.2.gemfile
|
21
|
-
|
22
|
-
before_install: gem update bundler
|
23
|
-
bundler_args: --jobs 3
|
24
|
-
install: bundle install --jobs 3
|
25
|
-
|
26
|
-
script:
|
27
|
-
- bash -c "if [ '$TRAVIS_RUBY_VERSION' = '3.0' ] && [[ '$BUNDLE_GEMFILE' == *'rails-6.1'* ]]; then bundle exec rubocop --fail-level autocorrect; fi"
|
28
|
-
- bundle exec rspec
|
data/Dockerfile
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
FROM instructure/rvm
|
2
|
-
|
3
|
-
WORKDIR /app
|
4
|
-
USER root
|
5
|
-
RUN chown -R docker:docker /app
|
6
|
-
RUN apt-get update
|
7
|
-
RUN apt-get install -y git
|
8
|
-
|
9
|
-
USER docker
|
10
|
-
|
11
|
-
COPY --chown=docker:docker dynamo-record.gemspec Gemfile /app/
|
12
|
-
COPY --chown=docker:docker lib/dynamo/record/version.rb /app/lib/dynamo/record/version.rb
|
13
|
-
|
14
|
-
RUN mkdir -p coverage \
|
15
|
-
spec/gemfiles/.bundle \
|
16
|
-
spec/internal/log
|
17
|
-
|
18
|
-
RUN bash -lc "rvm 2.6,2.7,3.0 do gem install --no-document bundler -v '~> 2.2'"
|
19
|
-
RUN bash -lc "cd /app && rvm-exec 2.6 bundle install --jobs 5"
|
20
|
-
RUN bash -lc "cd /app && rvm-exec 2.7 bundle install --jobs 5"
|
21
|
-
RUN bash -lc "cd /app && rvm-exec 3.0 bundle install --jobs 5"
|
22
|
-
COPY --chown=docker:docker . /app
|
23
|
-
|
24
|
-
CMD /bin/bash -lc "rvm-exec 3.0 bundle exec wwtd"
|
data/Jenkinsfile
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
#!/usr/bin/env groovy
|
2
|
-
|
3
|
-
pipeline {
|
4
|
-
agent {
|
5
|
-
label 'docker'
|
6
|
-
}
|
7
|
-
options {
|
8
|
-
ansiColor("xterm")
|
9
|
-
buildDiscarder(logRotator(numToKeepStr: '50'))
|
10
|
-
timeout(time: 20, unit: 'MINUTES')
|
11
|
-
}
|
12
|
-
stages {
|
13
|
-
stage('Build') {
|
14
|
-
steps {
|
15
|
-
sh 'docker-compose pull dynamo'
|
16
|
-
sh 'docker-compose up -d dynamo'
|
17
|
-
sh 'docker-compose build --pull app'
|
18
|
-
}
|
19
|
-
}
|
20
|
-
stage('Test') {
|
21
|
-
steps {
|
22
|
-
sh '''
|
23
|
-
docker-compose run --rm app /bin/bash -l -c \
|
24
|
-
"rvm-exec 3.0 bundle exec rubocop --fail-level autocorrect"
|
25
|
-
docker-compose run --name coverage app
|
26
|
-
'''
|
27
|
-
}
|
28
|
-
post {
|
29
|
-
always {
|
30
|
-
sh 'docker cp coverage:/app/coverage .'
|
31
|
-
sh 'docker-compose down --rmi=all --volumes --remove-orphans'
|
32
|
-
|
33
|
-
publishHTML target: [
|
34
|
-
allowMissing: false,
|
35
|
-
alwaysLinkToLastBuild: false,
|
36
|
-
keepAll: true,
|
37
|
-
reportDir: "coverage",
|
38
|
-
reportFiles: 'index.html',
|
39
|
-
reportName: 'Coverage Report'
|
40
|
-
]
|
41
|
-
}
|
42
|
-
}
|
43
|
-
}
|
44
|
-
}
|
45
|
-
}
|
data/LICENSE.txt
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (c) 2017 Instructure, Inc.
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|
@@ -1,19 +0,0 @@
|
|
1
|
-
version: '2'
|
2
|
-
|
3
|
-
services:
|
4
|
-
app:
|
5
|
-
volumes:
|
6
|
-
- .:/app
|
7
|
-
- gems:/home/docker/.rvm/gems
|
8
|
-
# Disable the rest of these volumes if the container can safely write to
|
9
|
-
# your host filesystem mount named above. You might want to use the rest
|
10
|
-
# of these unless you're using dinghy on OSX (usually needed for linux).
|
11
|
-
- coverage:/app/coverage
|
12
|
-
- bundle-config:/app/spec/gemfiles/.bundle
|
13
|
-
- internal-log:/app/spec/internal/log
|
14
|
-
|
15
|
-
volumes:
|
16
|
-
coverage: {}
|
17
|
-
bundle-config: {}
|
18
|
-
gems: {}
|
19
|
-
internal-log: {}
|
data/docker-compose.yml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
version: '2'
|
2
|
-
|
3
|
-
services:
|
4
|
-
app:
|
5
|
-
build: .
|
6
|
-
environment:
|
7
|
-
AWS_ACCESS_KEY_ID: x
|
8
|
-
AWS_SECRET_ACCESS_KEY: x
|
9
|
-
AWS_REGION: us-west-2
|
10
|
-
DYNAMO_ENDPOINT: http://dynamo:8000
|
11
|
-
RAILS_ENV: test
|
12
|
-
logging:
|
13
|
-
options:
|
14
|
-
max-file: '1'
|
15
|
-
max-size: 5m
|
16
|
-
links:
|
17
|
-
- dynamo
|
18
|
-
|
19
|
-
dynamo:
|
20
|
-
image: instructure/dynamodb
|
data/dynamo-record.gemspec
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path('lib', __dir__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require 'dynamo/record/version'
|
6
|
-
|
7
|
-
Gem::Specification.new do |s|
|
8
|
-
s.name = 'dynamo-record'
|
9
|
-
s.version = Dynamo::Record::VERSION
|
10
|
-
s.summary = 'Extensions to Aws::Record for working with DynamoDB.'
|
11
|
-
s.homepage = 'https://github.com/instructure/dynamo-record'
|
12
|
-
s.license = 'MIT'
|
13
|
-
|
14
|
-
s.authors = [
|
15
|
-
'Davis McClellan',
|
16
|
-
'Ryan Taylor',
|
17
|
-
'Bryan Petty',
|
18
|
-
'Michael Brewer-Davis',
|
19
|
-
'Marc Phillips',
|
20
|
-
'Augusto Callejas',
|
21
|
-
'Frank Murphy'
|
22
|
-
]
|
23
|
-
s.email = [
|
24
|
-
'dmcclellan@instructure.com',
|
25
|
-
'rtaylor@instructure.com',
|
26
|
-
'bpetty@instructure.com',
|
27
|
-
'mbd@instructure.com',
|
28
|
-
'mphillips@instructure.com',
|
29
|
-
'acallejas@instructure.com',
|
30
|
-
'fmurphy@instructure.com'
|
31
|
-
]
|
32
|
-
|
33
|
-
s.files = `git ls-files -z`.split("\x0").reject do |f|
|
34
|
-
f.match(%r{^(test|spec|features)/})
|
35
|
-
end
|
36
|
-
s.require_paths = ['lib']
|
37
|
-
|
38
|
-
s.required_ruby_version = '>= 2.6'
|
39
|
-
|
40
|
-
s.add_dependency 'activemodel', '>= 5.2', '< 6.2'
|
41
|
-
s.add_dependency 'aws-record', '~> 2.0'
|
42
|
-
s.add_dependency 'railties', '>= 5.2', '< 6.2'
|
43
|
-
|
44
|
-
s.add_development_dependency 'activesupport', '>= 5.2', '< 6.2'
|
45
|
-
s.add_development_dependency 'bundler', '~> 2.2'
|
46
|
-
s.add_development_dependency 'byebug', '~> 11.0'
|
47
|
-
s.add_development_dependency 'combustion', '~> 1.3'
|
48
|
-
s.add_development_dependency 'rake', '~> 13.0'
|
49
|
-
s.add_development_dependency 'rspec', '~> 3.6'
|
50
|
-
s.add_development_dependency 'rubocop', '~> 1.8.1'
|
51
|
-
s.add_development_dependency 'rubocop-rails', '~> 2.9.1'
|
52
|
-
s.add_development_dependency 'simplecov', '~> 0.16'
|
53
|
-
s.add_development_dependency 'webmock', '~> 3.3'
|
54
|
-
s.add_development_dependency 'wwtd', '~> 1.3'
|
55
|
-
end
|