inst_data_shipper 0.2.8 → 0.2.10.beta1
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/inst_data_shipper/data_sources/base.rb +7 -0
- data/lib/inst_data_shipper/data_sources/local_tables.rb +38 -16
- data/lib/inst_data_shipper/dumper.rb +0 -1
- data/lib/inst_data_shipper/jobs/base.rb +0 -0
- data/lib/inst_data_shipper/version.rb +1 -1
- data/spec/inst_data_shipper/data_sources/local_tables.rb +57 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: da87b45d1f05a92db75d71ce6fa414c4d30872bb2fe970f032151e0f0997378f
|
|
4
|
+
data.tar.gz: 25419ac15df71e1ec6e0d4b9dd0b3aca94ef5c812265386ce375cfb80c23d4f2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20a9ffd8cf6934beb86d8e52048707263ebf77a8f43f071899649851d345aeecd789e7029baa0a31cefd6785cb0e0dc4c260f09dedaba6c83d09008805cd2235
|
|
7
|
+
data.tar.gz: b280466a01b6986bf418e978fb8be1edc7fd4d783d72173055d3deb78790296ffb6d04bd74416d2b0c6947f74d9defd9f6d8897b6b1e9b17fb6b1c6cb380bdd4
|
|
@@ -45,6 +45,13 @@ module InstDataShipper
|
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
|
+
|
|
49
|
+
class MismatchingRowCounts < StandardError
|
|
50
|
+
def initialize(table_def={}, expected_row_count=0, processed_row_count=0)
|
|
51
|
+
message = "#{table_def[:warehouse_name]} expected #{expected_row_count} rows, processed #{processed_row_count} rows in local table."
|
|
52
|
+
super(message)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
48
55
|
end
|
|
49
56
|
end
|
|
50
57
|
end
|
|
@@ -11,23 +11,46 @@ module InstDataShipper
|
|
|
11
11
|
|
|
12
12
|
table_def = lookup_table_schema!(schema_name, { model: model })
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
string: ->(query, column) { query.where("#{column} > ?", incremental_since) },
|
|
23
|
-
default: "updated_at",
|
|
24
|
-
)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
process_raw_data_enumerator(table_def, query.find_each, file)
|
|
14
|
+
upload_data(table_def, &make_data_factory(model, table_def))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def make_data_factory(model, table_def)
|
|
18
|
+
->(file) {
|
|
19
|
+
query = build_query(model, table_def)
|
|
20
|
+
expected_row_count, processed_row_count = iterate_query(query, table_def, file)
|
|
21
|
+
raise MismatchingRowCounts.new(table_def, expected_row_count, processed_row_count) if !acceptable_row_counts_mismatch?(expected_row_count, processed_row_count)
|
|
28
22
|
}
|
|
23
|
+
end
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
def build_query(model, table_def)
|
|
26
|
+
query = model.all
|
|
27
|
+
query = _resolve_model_query(query, table_def[:query])
|
|
28
|
+
|
|
29
|
+
if table_is_incremental?(table_def)
|
|
30
|
+
query = _resolve_model_query(
|
|
31
|
+
query,
|
|
32
|
+
table_def.dig(:incremental, :scope),
|
|
33
|
+
string: ->(query, column) { query.where("#{column} > ?", incremental_since) },
|
|
34
|
+
default: "updated_at",
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
query
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def iterate_query(query, table_def, file)
|
|
41
|
+
processed_row_count = 0
|
|
42
|
+
expected_row_count = query.count
|
|
43
|
+
|
|
44
|
+
process_raw_data_enumerator(table_def, query.find_each.lazy.map { |x| processed_row_count += 1; x }, file)
|
|
45
|
+
[expected_row_count, processed_row_count]
|
|
46
|
+
end
|
|
47
|
+
#
|
|
48
|
+
# The count of rows in the database and the exported files may not always match. We accept a small range difference
|
|
49
|
+
def acceptable_row_counts_mismatch?(expected_row_count, processed_row_count, variation: 0.01)
|
|
50
|
+
return true if expected_row_count.nil?
|
|
51
|
+
|
|
52
|
+
expected_row_count = expected_row_count.to_f
|
|
53
|
+
expected_row_count * (1 - variation) <= processed_row_count && processed_row_count <= expected_row_count * (1 + variation)
|
|
31
54
|
end
|
|
32
55
|
|
|
33
56
|
private
|
|
@@ -47,7 +70,6 @@ module InstDataShipper
|
|
|
47
70
|
raise "Invalid query: #{query.inspect}"
|
|
48
71
|
end
|
|
49
72
|
end
|
|
50
|
-
|
|
51
73
|
end
|
|
52
74
|
end
|
|
53
75
|
end
|
|
@@ -272,7 +272,6 @@ module InstDataShipper
|
|
|
272
272
|
dest.upload_data_chunk(table_def, chunk)
|
|
273
273
|
end
|
|
274
274
|
end
|
|
275
|
-
|
|
276
275
|
# TODO Consider how to handle errors in this method.
|
|
277
276
|
# Retriable errors must not be allowed to bubble - if dest 1 succeeds and dest 2 fails, dest 1 must not be retried
|
|
278
277
|
# Each destination should handle its own retries
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe InstDataShipper::DataSources::LocalTables do
|
|
4
|
+
let(:dumper) do
|
|
5
|
+
dumper = TestDumper.new
|
|
6
|
+
dumper.extend(InstDataShipper::DataSources::LocalTables)
|
|
7
|
+
dumper
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "#import_local_table" do
|
|
11
|
+
before do
|
|
12
|
+
allow(dumper).to receive(:iterate_query).and_return([5, 5])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "counts expected rows and processes data" do
|
|
16
|
+
allow(dumper).to receive(:upload_data) do |td, &make_data_factory|
|
|
17
|
+
make_data_factory.call("filename")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
expect { dumper.send(:_delayed_import_local_table, InstDataShipper::DumpBatch, schema_name: "local_table_test") }.not_to raise_error
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#make_data_factory' do
|
|
25
|
+
before do
|
|
26
|
+
allow(dumper).to receive(:build_query).and_return(true)
|
|
27
|
+
end
|
|
28
|
+
it 'raises an error when expected row count does not match the processed count' do
|
|
29
|
+
allow(dumper).to receive(:iterate_query).and_return([50, 100])
|
|
30
|
+
expect { dumper.make_data_factory(InstDataShipper::DumpBatch, {}).call("filename") }.to raise_error(InstDataShipper::DataSources::Base::MismatchingRowCounts)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'does not raise an error when the expected count matches the processed count' do
|
|
34
|
+
allow(dumper).to receive(:iterate_query).and_return([50, 50])
|
|
35
|
+
expect { dumper.make_data_factory(InstDataShipper::DumpBatch, {}).call("filename") }.not_to raise_error(InstDataShipper::DataSources::Base::MismatchingRowCounts)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '#acceptable_row_counts_mismatch?' do
|
|
40
|
+
it 'returns whether the counts are within the range or not' do
|
|
41
|
+
expect(dumper.acceptable_row_counts_mismatch?(100, 105, variation: 0.01)).to eq(false)
|
|
42
|
+
expect(dumper.acceptable_row_counts_mismatch?(100, 100, variation: 0.01)).to eq(true)
|
|
43
|
+
expect(dumper.acceptable_row_counts_mismatch?(100, 105, variation: 0.1)).to eq(true)
|
|
44
|
+
expect(dumper.acceptable_row_counts_mismatch?(100, 111, variation: 0.1)).to eq(false)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it 'returns false for the upper/lower bound' do
|
|
48
|
+
# current implementation accepts the upper / lower bound
|
|
49
|
+
expect(dumper.acceptable_row_counts_mismatch?(100, 101, variation: 0.01)).to eq(true)
|
|
50
|
+
expect(dumper.acceptable_row_counts_mismatch?(100, 99, variation: 0.01)).to eq(true)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'returns true for 0 rows expected and 0 rows processed' do
|
|
54
|
+
expect(dumper.acceptable_row_counts_mismatch?(0, 0, variation: 0.01)).to eq(true)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inst_data_shipper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.10.beta1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Instructure CustomDev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -361,6 +361,7 @@ files:
|
|
|
361
361
|
- lib/inst_data_shipper/record.rb
|
|
362
362
|
- lib/inst_data_shipper/schema_builder.rb
|
|
363
363
|
- lib/inst_data_shipper/version.rb
|
|
364
|
+
- spec/inst_data_shipper/data_sources/local_tables.rb
|
|
364
365
|
- spec/inst_data_shipper/destinations/hosted_data_spec.rb
|
|
365
366
|
- spec/inst_data_shipper/dumper_spec.rb
|
|
366
367
|
- spec/internal/app/assets/config/manifest.js
|
|
@@ -396,6 +397,7 @@ signing_key:
|
|
|
396
397
|
specification_version: 4
|
|
397
398
|
summary: Gem for uploading app data to Instructure CustomDev Hosted Data tooling
|
|
398
399
|
test_files:
|
|
400
|
+
- spec/inst_data_shipper/data_sources/local_tables.rb
|
|
399
401
|
- spec/inst_data_shipper/destinations/hosted_data_spec.rb
|
|
400
402
|
- spec/inst_data_shipper/dumper_spec.rb
|
|
401
403
|
- spec/internal/app/assets/config/manifest.js
|