ncs_mdes_warehouse 0.0.2 → 0.1.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.
- data/CHANGELOG.md +16 -0
- data/generated_models/ncs_navigator/warehouse/models/two_point_zero/env_equipment_prob_log.rb +1 -1
- data/generated_models/ncs_navigator/warehouse/models/two_point_zero/incident.rb +6 -6
- data/generated_models/ncs_navigator/warehouse/models/two_point_zero/participant_rvis.rb +1 -1
- data/generated_models/ncs_navigator/warehouse/models/two_point_zero/sample_shipping.rb +1 -1
- data/lib/ncs_navigator/warehouse.rb +4 -0
- data/lib/ncs_navigator/warehouse/cli.rb +31 -1
- data/lib/ncs_navigator/warehouse/configuration.rb +49 -9
- data/lib/ncs_navigator/warehouse/database_initializer.rb +62 -4
- data/lib/ncs_navigator/warehouse/models.rb +3 -0
- data/lib/ncs_navigator/warehouse/postgresql.rb +7 -0
- data/lib/ncs_navigator/warehouse/postgresql/pgpass.rb +79 -0
- data/lib/ncs_navigator/warehouse/table_modeler/mdes_ext.rb +9 -0
- data/lib/ncs_navigator/warehouse/table_modeler/model_template.rb.erb +1 -1
- data/lib/ncs_navigator/warehouse/transform_load.rb +55 -0
- data/lib/ncs_navigator/warehouse/transform_status.rb +63 -0
- data/lib/ncs_navigator/warehouse/transformers.rb +0 -1
- data/lib/ncs_navigator/warehouse/transformers/database.rb +91 -85
- data/lib/ncs_navigator/warehouse/transformers/enum_transformer.rb +26 -8
- data/lib/ncs_navigator/warehouse/transformers/vdr_xml.rb +1 -1
- data/lib/ncs_navigator/warehouse/transformers/vdr_xml/reader.rb +11 -4
- data/lib/ncs_navigator/warehouse/version.rb +1 -1
- data/spec/bcdatabase/test_sqlite.yml +4 -0
- data/spec/ncs_navigator/warehouse/configuration_spec.rb +42 -0
- data/spec/ncs_navigator/warehouse/postgresql/pgpass_spec.rb +187 -0
- data/spec/ncs_navigator/warehouse/table_modeler_spec.rb +15 -1
- data/spec/ncs_navigator/warehouse/transform_load_spec.rb +152 -0
- data/spec/ncs_navigator/warehouse/transformers/database_spec.rb +24 -28
- data/spec/ncs_navigator/warehouse/transformers/enum_transformer_spec.rb +16 -10
- data/spec/ncs_navigator/warehouse/transformers/vdr_xml/made_up_vdr_xml.xml +4 -4
- data/spec/ncs_navigator/warehouse/transformers/vdr_xml/reader_spec.rb +8 -3
- data/spec/spec_helper.rb +1 -1
- metadata +44 -37
- data/lib/ncs_navigator/warehouse/transformers/transform_status.rb +0 -23
@@ -9,7 +9,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
9
9
|
# @return [#transform] a transformer that loads the MDES data in
|
10
10
|
# the specified VDR XML file.
|
11
11
|
def from_file(config, filename) # <- TODO better solution
|
12
|
-
EnumTransformer.new(Reader.new(config, filename))
|
12
|
+
EnumTransformer.new(config, Reader.new(config, filename))
|
13
13
|
end
|
14
14
|
|
15
15
|
##
|
@@ -46,8 +46,9 @@ class NcsNavigator::Warehouse::Transformers::VdrXml
|
|
46
46
|
@start = Time.now
|
47
47
|
|
48
48
|
Nokogiri::XML::Reader(@io).each do |node|
|
49
|
-
|
50
|
-
|
49
|
+
element_types = [:TYPE_ELEMENT, :TYPE_END_ELEMENT].
|
50
|
+
collect { |c| Nokogiri::XML::Reader.const_get(c) }
|
51
|
+
next unless element_types.include?(node.node_type)
|
51
52
|
encounter_node(node, node.node_type == 1, &block)
|
52
53
|
end
|
53
54
|
|
@@ -77,8 +78,14 @@ class NcsNavigator::Warehouse::Transformers::VdrXml
|
|
77
78
|
# node is the start tag of a table variable
|
78
79
|
var = node.local_name.to_sym
|
79
80
|
val = node.inner_xml.strip
|
80
|
-
|
81
|
-
node.
|
81
|
+
|
82
|
+
unless node.self_closing?
|
83
|
+
# Skip to closing tag
|
84
|
+
n = node.read
|
85
|
+
until n.node_type == Nokogiri::XML::Reader::TYPE_END_ELEMENT
|
86
|
+
n = node.read
|
87
|
+
end
|
88
|
+
end
|
82
89
|
|
83
90
|
unless should_filter_out(@current_model_class, var, val)
|
84
91
|
@current_parameter_values[var] = val
|
@@ -328,6 +328,48 @@ module NcsNavigator::Warehouse
|
|
328
328
|
end
|
329
329
|
end
|
330
330
|
|
331
|
+
describe '#pg_bin_path' do
|
332
|
+
it 'defaults to nil' do
|
333
|
+
config.pg_bin_path.should be_nil
|
334
|
+
end
|
335
|
+
|
336
|
+
describe 'when set' do
|
337
|
+
before do
|
338
|
+
config.pg_bin_path = '/Library/PostgreSQL/9.0/bin'
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'is retrievable' do
|
342
|
+
config.pg_bin_path.to_s.should == '/Library/PostgreSQL/9.0/bin'
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'becomes a Pathname' do
|
346
|
+
config.pg_bin_path.should be_a Pathname
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
describe '#pg_bin' do
|
352
|
+
describe 'with no path' do
|
353
|
+
before do
|
354
|
+
config.pg_bin_path = nil
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'prepends nothing' do
|
358
|
+
config.pg_bin('pg_dumpall').to_s.should == 'pg_dumpall'
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe 'with a path' do
|
363
|
+
before do
|
364
|
+
config.pg_bin_path = '/home/me/bin'
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'prepends the path' do
|
368
|
+
config.pg_bin('pg_dumpall').to_s.should == '/home/me/bin/pg_dumpall'
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
331
373
|
describe '.from_file' do
|
332
374
|
let(:filename) { tmpdir + 'test.rb' }
|
333
375
|
subject { Configuration.from_file(filename) }
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require File.expand_path('../../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
module NcsNavigator::Warehouse::PostgreSQL
|
4
|
+
describe Pgpass do
|
5
|
+
let(:home) { tmpdir('home') }
|
6
|
+
let(:file) { home + '.pgpass' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
@original_home, ENV['HOME'] = ENV['HOME'], home
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
ENV['HOME'] = @original_home
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#file' do
|
17
|
+
it 'defaults to $HOME/.pgpass' do
|
18
|
+
Pgpass.new.file.should == file
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '.line' do
|
23
|
+
subject { Pgpass.line entry }
|
24
|
+
let(:entry) {
|
25
|
+
{
|
26
|
+
'username' => 'warehouse',
|
27
|
+
'password' => 'volt',
|
28
|
+
'database' => 'ignored'
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
it 'includes defaults for host and port if not set' do
|
33
|
+
subject.should == %w(localhost 5432 * warehouse volt)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'uses an explicit host if available' do
|
37
|
+
entry['host'] = 'ncs_db'
|
38
|
+
subject.should == %w(ncs_db 5432 * warehouse volt)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'uses an explicit port if available' do
|
42
|
+
entry['port'] = 5439
|
43
|
+
subject.should == %w(localhost 5439 * warehouse volt)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'fails without a username' do
|
47
|
+
entry.delete 'username'
|
48
|
+
lambda { subject }.should raise_error(/No username in configuration/)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'fails without a password' do
|
52
|
+
entry.delete 'password'
|
53
|
+
lambda { subject }.should raise_error(/No password in configuration/)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#update' do
|
58
|
+
subject { Pgpass.new }
|
59
|
+
let(:entry) {
|
60
|
+
{
|
61
|
+
'username' => 'mw',
|
62
|
+
'password' => 'fishes',
|
63
|
+
'host' => 'ncsdb',
|
64
|
+
'port' => 5500,
|
65
|
+
'database' => 'mw_r'
|
66
|
+
}
|
67
|
+
}
|
68
|
+
let(:lines) {
|
69
|
+
file.readlines.collect { |l| l.chomp.split ':' }
|
70
|
+
}
|
71
|
+
let(:expected_line) { "ncsdb:5500:*:mw:fishes\n" }
|
72
|
+
|
73
|
+
describe 'when the file exists' do
|
74
|
+
describe 'and the same line exists' do
|
75
|
+
before do
|
76
|
+
file.open('w') do |f|
|
77
|
+
f.puts 'somewhere:5400:*:aguy:five'
|
78
|
+
f.puts 'ncsdb:5500:*:mw:fishes'
|
79
|
+
f.puts 'somewhere:5400:*:someoneelse:yet'
|
80
|
+
end
|
81
|
+
|
82
|
+
subject.update(entry)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'does nothing' do
|
86
|
+
lines.collect { |l| l[3] }.should == %w(aguy mw someoneelse)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'and a similar line with a different password exists' do
|
91
|
+
before do
|
92
|
+
file.open('w') do |f|
|
93
|
+
f.puts 'somewhere:5400:*:aguy:five'
|
94
|
+
f.puts 'ncsdb:5500:*:mw:shark'
|
95
|
+
f.puts 'ncsdb:5500:*:someoneelse:fred'
|
96
|
+
f.puts 'somewhere:5400:*:someoneelse:yet'
|
97
|
+
end
|
98
|
+
|
99
|
+
subject.update(entry)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'updates the password' do
|
103
|
+
file.readlines[1].should == expected_line
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'leaves other entries alone' do
|
107
|
+
lines.collect { |l| l[4] }.should == %w(five fishes fred yet)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'and no similar entry exists' do
|
112
|
+
before do
|
113
|
+
file.open('w') do |f|
|
114
|
+
f.puts 'somewhere:5400:*:aguy:five'
|
115
|
+
f.puts 'ncsdb:5500:*:someoneelse:fred'
|
116
|
+
f.puts 'somewhere:5400:*:someoneelse:yet'
|
117
|
+
end
|
118
|
+
|
119
|
+
subject.update(entry)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'adds the entry' do
|
123
|
+
file.readlines.last.should == expected_line
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'leaves the other entries alone' do
|
127
|
+
lines.collect { |l| l[0] }.should == %w(somewhere ncsdb somewhere ncsdb)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'when the file does not exist' do
|
133
|
+
before do
|
134
|
+
file.exist?.should be_false
|
135
|
+
|
136
|
+
subject.update(entry)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'creates the file' do
|
140
|
+
file.exist?.should be_true
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'sets the permissions to user-rw only' do
|
144
|
+
`ls -al '#{file}'`.should =~ /^-rw------/
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'adds the appropriate entry' do
|
148
|
+
file.readlines.should == [expected_line]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe 'when the directory does not exist' do
|
153
|
+
before do
|
154
|
+
home.rmdir
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'fails' do
|
158
|
+
lambda { subject.update(entry) }.should(
|
159
|
+
raise_error %r{Cannot create #{home}/.pgpass})
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe 'when the directory is not writable' do
|
164
|
+
before do
|
165
|
+
home.chmod(0500)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'fails' do
|
169
|
+
lambda { subject.update(entry) }.should(
|
170
|
+
raise_error %r{Cannot create #{home}/.pgpass})
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'when the file exists but is not writable' do
|
175
|
+
before do
|
176
|
+
file.open('w') { |f| }
|
177
|
+
file.chmod(0500)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'fails' do
|
181
|
+
lambda { subject.update(entry) }.should(
|
182
|
+
raise_error %r{Cannot update #{home}/.pgpass})
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -449,8 +449,10 @@ end
|
|
449
449
|
end
|
450
450
|
end
|
451
451
|
|
452
|
+
let(:child_variable_name) { 'some_tree_id' }
|
453
|
+
|
452
454
|
before do
|
453
|
-
table.variables << NcsNavigator::Mdes::Variable.new(
|
455
|
+
table.variables << NcsNavigator::Mdes::Variable.new(child_variable_name).tap do |v|
|
454
456
|
v.table_reference = parent_table
|
455
457
|
v.type = NcsNavigator::Mdes::VariableType.new('foreignKeyRequiredType').tap do |vt|
|
456
458
|
vt.base_type = :string
|
@@ -502,6 +504,18 @@ end
|
|
502
504
|
|
503
505
|
instance.should_not be_valid
|
504
506
|
end
|
507
|
+
|
508
|
+
describe 'when the child key does not have the "_id" suffix' do
|
509
|
+
let(:child_variable_name) { 'some_tree' }
|
510
|
+
|
511
|
+
it 'has an object reference accessor' do
|
512
|
+
model_class.instance_methods.collect(&:to_s).should include('some_tree_record')
|
513
|
+
end
|
514
|
+
|
515
|
+
it 'does not have a suffixed ID attribute' do
|
516
|
+
model_class.instance_methods.collect(&:to_s).should_not include('some_tree_id')
|
517
|
+
end
|
518
|
+
end
|
505
519
|
end
|
506
520
|
end
|
507
521
|
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
module NcsNavigator::Warehouse
|
4
|
+
describe TransformLoad, :modifies_warehouse_state do
|
5
|
+
let(:config) { base_config }
|
6
|
+
let(:loader) { TransformLoad.new(config) }
|
7
|
+
|
8
|
+
def base_config
|
9
|
+
Configuration.new.tap do |c|
|
10
|
+
c.bcdatabase_group = :test_sqlite
|
11
|
+
c.log_file = tmpdir + "#{File.basename(__FILE__)}.log"
|
12
|
+
c.output_level = :quiet
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:all) do
|
17
|
+
# Not config due to RSpec #500
|
18
|
+
DatabaseInitializer.new(base_config).tap do |dbi|
|
19
|
+
dbi.set_up_repository(:both)
|
20
|
+
end
|
21
|
+
TransformError.auto_migrate!(:mdes_warehouse_working)
|
22
|
+
TransformStatus.auto_migrate!(:mdes_warehouse_working)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#run' do
|
26
|
+
it 'runs the transformers in the order specified' do
|
27
|
+
order = []
|
28
|
+
config.add_transformer(BlockTransformer.new { |s| order << 'B' })
|
29
|
+
config.add_transformer(BlockTransformer.new { |s| order << 'T' })
|
30
|
+
config.add_transformer(BlockTransformer.new { |s| order << 'w' })
|
31
|
+
|
32
|
+
loader.run
|
33
|
+
|
34
|
+
order.should == %w(B T w)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'passes a separate instance of TransformStatus to each transformer' do
|
38
|
+
statuses = []
|
39
|
+
config.add_transformer(BlockTransformer.new { |s| statuses << s })
|
40
|
+
config.add_transformer(BlockTransformer.new { |s| statuses << s })
|
41
|
+
|
42
|
+
loader.run
|
43
|
+
|
44
|
+
statuses.first.should_not be statuses.last
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'a created status' do
|
48
|
+
let(:statuses) { [] }
|
49
|
+
let(:status) { statuses.first }
|
50
|
+
let(:transformer) { BlockTransformer.new { |s| statuses << s } }
|
51
|
+
|
52
|
+
before do
|
53
|
+
config.add_transformer(transformer)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'uses the name provided by the transformer, if any' do
|
57
|
+
def transformer.name; 'provided'; end
|
58
|
+
|
59
|
+
loader.run
|
60
|
+
|
61
|
+
status.name.should == 'provided'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'uses the class name of the transformer otherwise' do
|
65
|
+
loader.run
|
66
|
+
|
67
|
+
status.name.should == 'BlockTransformer'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'sets the start and end times' do
|
71
|
+
Time.should_receive(:now).and_return(Time.iso8601('2001-01-12T08:00:00'))
|
72
|
+
Time.stub!(:now).and_return(Time.iso8601('2001-01-12T09:06:30'))
|
73
|
+
|
74
|
+
loader.run
|
75
|
+
|
76
|
+
status.start_time.hour.should == 8
|
77
|
+
status.end_time.hour.should == 9
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sets the position' do
|
81
|
+
loader.run
|
82
|
+
status.position.should == 0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'returns true if none of the transformers report an error' do
|
87
|
+
config.add_transformer(BlockTransformer.new { |s| })
|
88
|
+
loader.run.should be_true
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'with a reported error' do
|
92
|
+
let(:runs) { [] }
|
93
|
+
|
94
|
+
before do
|
95
|
+
config.add_transformer(BlockTransformer.new { |s| runs << 'A' })
|
96
|
+
config.add_transformer(BlockTransformer.new { |s|
|
97
|
+
runs << 'B'
|
98
|
+
s.unsuccessful_record(nil, 'fail')
|
99
|
+
})
|
100
|
+
config.add_transformer(BlockTransformer.new { |s| runs << 'C3' })
|
101
|
+
@result = loader.run
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'still runs all the transformers' do
|
105
|
+
runs.should == %w(A B C3)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'returns false' do
|
109
|
+
@result.should == false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'with a failure' do
|
114
|
+
let(:runs) { [] }
|
115
|
+
|
116
|
+
before do
|
117
|
+
config.add_transformer(BlockTransformer.new { |s| runs << 'A' })
|
118
|
+
config.add_transformer(BlockTransformer.new { |s|
|
119
|
+
runs << 'B'
|
120
|
+
fail 'No thanks.'
|
121
|
+
})
|
122
|
+
config.add_transformer(BlockTransformer.new { |s| runs << 'C3' })
|
123
|
+
@result = loader.run
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'still runs all the transformers' do
|
127
|
+
runs.should == %w(A B C3)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'returns false' do
|
131
|
+
@result.should == false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'sending e-mail' do
|
136
|
+
it 'sends on success'
|
137
|
+
|
138
|
+
it 'sends on failure'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class ::BlockTransformer
|
143
|
+
def initialize(&block)
|
144
|
+
@block = block
|
145
|
+
end
|
146
|
+
|
147
|
+
def transform(status)
|
148
|
+
@block.call(status)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|