ncs_mdes_warehouse 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|