topographer 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +38 -0
- data/Rakefile +1 -0
- data/lib/Topographer/exceptions.rb +4 -0
- data/lib/Topographer/importer.rb +57 -0
- data/lib/Topographer/importer/helpers.rb +18 -0
- data/lib/Topographer/importer/helpers/write_log_to_csv.rb +85 -0
- data/lib/Topographer/importer/importable.rb +5 -0
- data/lib/Topographer/importer/input.rb +5 -0
- data/lib/Topographer/importer/input/base.rb +13 -0
- data/lib/Topographer/importer/input/roo.rb +28 -0
- data/lib/Topographer/importer/input/source_data.rb +8 -0
- data/lib/Topographer/importer/logger.rb +8 -0
- data/lib/Topographer/importer/logger/base.rb +69 -0
- data/lib/Topographer/importer/logger/fatal_error_entry.rb +19 -0
- data/lib/Topographer/importer/logger/file.rb +0 -0
- data/lib/Topographer/importer/logger/log_entry.rb +34 -0
- data/lib/Topographer/importer/logger/simple.rb +27 -0
- data/lib/Topographer/importer/mapper.rb +161 -0
- data/lib/Topographer/importer/mapper/default_field_mapping.rb +22 -0
- data/lib/Topographer/importer/mapper/field_mapping.rb +55 -0
- data/lib/Topographer/importer/mapper/ignored_field_mapping.rb +10 -0
- data/lib/Topographer/importer/mapper/result.rb +21 -0
- data/lib/Topographer/importer/mapper/validation_field_mapping.rb +36 -0
- data/lib/Topographer/importer/strategy.rb +7 -0
- data/lib/Topographer/importer/strategy/base.rb +42 -0
- data/lib/Topographer/importer/strategy/create_or_update_record.rb +50 -0
- data/lib/Topographer/importer/strategy/import_new_record.rb +17 -0
- data/lib/Topographer/importer/strategy/import_status.rb +28 -0
- data/lib/Topographer/importer/strategy/update_record.rb +33 -0
- data/lib/Topographer/version.rb +3 -0
- data/lib/topographer.rb +6 -0
- data/spec/Cartographer/importer/helpers/write_log_to_csv_spec.rb +69 -0
- data/spec/Cartographer/importer/helpers_spec.rb +33 -0
- data/spec/Cartographer/importer/importable_spec.rb +13 -0
- data/spec/Cartographer/importer/importer_spec.rb +132 -0
- data/spec/Cartographer/importer/logger/base_spec.rb +12 -0
- data/spec/Cartographer/importer/logger/fatal_error_entry_spec.rb +31 -0
- data/spec/Cartographer/importer/logger/simple_spec.rb +53 -0
- data/spec/Cartographer/importer/mapper/default_field_mapping_spec.rb +41 -0
- data/spec/Cartographer/importer/mapper/field_mapping_spec.rb +126 -0
- data/spec/Cartographer/importer/mapper/validation_field_mapping_spec.rb +42 -0
- data/spec/Cartographer/importer/mapper_spec.rb +318 -0
- data/spec/Cartographer/importer/strategy/base_spec.rb +43 -0
- data/spec/Cartographer/importer/strategy/create_or_update_record_spec.rb +46 -0
- data/spec/Cartographer/importer/strategy/import_new_records_spec.rb +66 -0
- data/spec/Cartographer/importer/strategy/import_status_spec.rb +24 -0
- data/spec/Cartographer/importer/strategy/mapped_model.rb +41 -0
- data/spec/Cartographer/importer/strategy/update_record_spec.rb +45 -0
- data/spec/spec_helper.rb +1 -0
- data/topographer.gemspec +26 -0
- metadata +175 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Importer::Mapper::DefaultFieldMapping do
|
4
|
+
let(:static_mapping) do
|
5
|
+
Importer::Mapper::DefaultFieldMapping.new('field1') do
|
6
|
+
10+5
|
7
|
+
end
|
8
|
+
end
|
9
|
+
let(:failed_static_mapping) do
|
10
|
+
Importer::Mapper::DefaultFieldMapping.new('field1') do
|
11
|
+
raise 'FAILURE'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:result) { Importer::Mapper::Result.new('test') }
|
16
|
+
let(:result2) { Importer::Mapper::Result.new('test') }
|
17
|
+
describe '#initialize' do
|
18
|
+
it 'should not create a static mapping without a behavior block' do
|
19
|
+
expect { Importer::Mapper::DefaultFieldMapping.new('broken mapping') }.
|
20
|
+
to raise_error(Topographer::InvalidMappingError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
describe '#process_input' do
|
24
|
+
it 'should return the result of the behavior block' do
|
25
|
+
static_mapping.process_input({}, result)
|
26
|
+
expect(result.errors?).to be_false
|
27
|
+
expect(result.data['field1']).to be 15
|
28
|
+
end
|
29
|
+
it 'should record any exceptions that occur within the block as errors' do
|
30
|
+
failed_static_mapping.process_input({}, result)
|
31
|
+
expect(result.errors?).to be_true
|
32
|
+
expect(result.errors.values).to include('FAILURE')
|
33
|
+
end
|
34
|
+
it 'should not rescue Exceptions that do not inherit from standard error' do
|
35
|
+
mapper = Importer::Mapper::DefaultFieldMapping.new('output_column') do
|
36
|
+
raise Exception, 'Field1 MUST BE 4'
|
37
|
+
end
|
38
|
+
expect{ mapper.process_input({'field1' => false}, result) }.to raise_error(Exception)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Importer::Mapper::FieldMapping do
|
4
|
+
let(:required_simple_mapping) { Importer::Mapper::FieldMapping.new(true, ['field1'], 'output_column') }
|
5
|
+
let(:required_simple_mapping_with_validation) do
|
6
|
+
Importer::Mapper::FieldMapping.new(true, ['field1'], 'output_column') do |input|
|
7
|
+
if input['field1'] != 4
|
8
|
+
raise 'Field1 MUST BE 4'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
let(:required_complex_mapping) do
|
13
|
+
Importer::Mapper::FieldMapping.new(true, ['field1', 'field2', 'field3'], 'output_column') do |input|
|
14
|
+
if input['field1'] != 4
|
15
|
+
raise 'Field1 MUST BE 4'
|
16
|
+
end
|
17
|
+
input.values.flatten.inject(0) {|sum, x| sum+x}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
let(:optional_simple_mapping) { Importer::Mapper::FieldMapping.new(false, 'field1', 'output_column') }
|
21
|
+
let(:optional_complex_mapping) do
|
22
|
+
Importer::Mapper::FieldMapping.new(false, ['field1', 'field2', 'field3'], 'output_column') do |input|
|
23
|
+
if input['field1'] != 4
|
24
|
+
raise 'Field1 MUST BE 4'
|
25
|
+
end
|
26
|
+
input.values.flatten.inject(0) {|sum, x| sum+x}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
let(:valid_input) do
|
30
|
+
{'field1' => 4, 'field2' => 5, 'field3' => 6}
|
31
|
+
end
|
32
|
+
let(:invalid_complex_input) do
|
33
|
+
{'field1' => 3, 'field2' => 4, 'field3' => 5}
|
34
|
+
end
|
35
|
+
let(:result) { Importer::Mapper::Result.new('test') }
|
36
|
+
describe '#process_input' do
|
37
|
+
context 'required mappings' do
|
38
|
+
it 'maps required simple mappings when input is valid' do
|
39
|
+
required_simple_mapping.process_input(valid_input, result)
|
40
|
+
expect(result.errors?).to be_false
|
41
|
+
expect(result.data['output_column']).to eql(4)
|
42
|
+
end
|
43
|
+
it 'maps required complex mappings when input is valid' do
|
44
|
+
required_complex_mapping.process_input(valid_input, result)
|
45
|
+
expect(result.errors?).to be_false
|
46
|
+
expect(result.data['output_column']).to eql(15)
|
47
|
+
end
|
48
|
+
it 'returns an error for required simple mappings when the input key is missing' do
|
49
|
+
required_simple_mapping.process_input({}, result)
|
50
|
+
expect(result.errors?).to be_true
|
51
|
+
expect(result.errors.values).to include('Missing required input(s): `field1` for `output_column`')
|
52
|
+
expect(result.data.empty?).to be_true
|
53
|
+
end
|
54
|
+
it 'returns an error for required simple mappings when the input data is blank' do
|
55
|
+
required_simple_mapping.process_input({'field1' => nil}, result)
|
56
|
+
expect(result.errors?).to be_true
|
57
|
+
expect(result.errors.values).to include('Missing required input(s): `field1` for `output_column`')
|
58
|
+
expect(result.data['output_column']).to be_nil
|
59
|
+
end
|
60
|
+
it 'returns an error for required simple mappings when the mapping block raises an exception' do
|
61
|
+
required_simple_mapping_with_validation.process_input({'field1' => 3}, result)
|
62
|
+
expect(result.errors?).to be_true
|
63
|
+
expect(result.errors.values).to include('Field1 MUST BE 4')
|
64
|
+
expect(result.data.empty?).to be_true
|
65
|
+
end
|
66
|
+
it 'returns an error for required complex mappings when the input key is missing' do
|
67
|
+
required_complex_mapping.process_input({'field1' => 4}, result)
|
68
|
+
expect(result.errors?).to be_true
|
69
|
+
expect(result.errors.values).to include('Missing required input(s): `field2, field3` for `output_column`')
|
70
|
+
expect(result.data.empty?).to be_true
|
71
|
+
end
|
72
|
+
it 'returns an error for required complex mappings when the mapping block raises an exception' do
|
73
|
+
required_complex_mapping.process_input(invalid_complex_input, result)
|
74
|
+
expect(result.errors?).to be_true
|
75
|
+
expect(result.errors.values).to include('Field1 MUST BE 4')
|
76
|
+
expect(result.data.empty?).to be_true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
context 'optional mappings' do
|
80
|
+
it 'maps optional simple mappings when input is valid' do
|
81
|
+
optional_simple_mapping.process_input(valid_input, result)
|
82
|
+
expect(result.errors?).to be_false
|
83
|
+
expect(result.data['output_column']).to eql(4)
|
84
|
+
end
|
85
|
+
it 'maps optional complex mappings when input is valid' do
|
86
|
+
optional_complex_mapping.process_input(valid_input, result)
|
87
|
+
expect(result.errors?).to be_false
|
88
|
+
expect(result.data['output_column']).to eql(15)
|
89
|
+
end
|
90
|
+
it 'does not return an error for optional simple mappings when the input key is missing' do
|
91
|
+
optional_simple_mapping.process_input({}, result)
|
92
|
+
expect(result.errors?).to be_false
|
93
|
+
expect(result.data.empty?).to be_true
|
94
|
+
end
|
95
|
+
it 'does not return an error for optional simple mappings when the input data is blank' do
|
96
|
+
optional_simple_mapping.process_input({'field1' => nil}, result)
|
97
|
+
expect(result.errors?).to be_false
|
98
|
+
expect(result.data['output_column']).to be_nil
|
99
|
+
end
|
100
|
+
it 'does not return an error for optional complex mappings when an input key is missing' do
|
101
|
+
optional_complex_mapping.process_input({'field1' => 4}, result)
|
102
|
+
expect(result.errors?).to be_false
|
103
|
+
expect(result.data.empty?).to be_true
|
104
|
+
end
|
105
|
+
it 'returns an error for optional complex mappings when the mapping block raises an exception' do
|
106
|
+
optional_complex_mapping.process_input(invalid_complex_input, result)
|
107
|
+
expect(result.errors?).to be_true
|
108
|
+
expect(result.errors.values).to include('Field1 MUST BE 4')
|
109
|
+
expect(result.data.empty?).to be_true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
it 'maps data when the result of the mapping is a false value' do
|
113
|
+
#required_simple_mapping.stub(:apply_mapping).and_return(false)
|
114
|
+
required_simple_mapping.process_input({'field1' => false}, result)
|
115
|
+
expect(result.errors?).to be_false
|
116
|
+
expect(result.data['output_column']).to eql(false)
|
117
|
+
end
|
118
|
+
it 'should not rescue Exceptions that do not inherit from standard error' do
|
119
|
+
mapper = Importer::Mapper::FieldMapping.new(true, 'field1', 'output_column') do |input|
|
120
|
+
raise Exception, 'Field1 MUST BE 4'
|
121
|
+
end
|
122
|
+
expect{ mapper.process_input({'field1' => false}, result) }.to raise_error(Exception)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Importer::Mapper::ValidationFieldMapping do
|
4
|
+
let(:validation_mapping) do
|
5
|
+
Importer::Mapper::ValidationFieldMapping.new('test mapping', ['field1', 'field2', 'field3']) do |input|
|
6
|
+
sum = input.values.flatten.inject(0) {|sum, x| sum+x}
|
7
|
+
raise 'Sum must be 15' if sum != 15
|
8
|
+
end
|
9
|
+
end
|
10
|
+
let(:valid_input) do
|
11
|
+
{'field1' => 4, 'field2' => 5, 'field3' => 6}
|
12
|
+
end
|
13
|
+
let(:invalid_input) do
|
14
|
+
{'field1' => 3, 'field2' => 4, 'field3' => 5}
|
15
|
+
end
|
16
|
+
let(:result) { Importer::Mapper::Result.new('test') }
|
17
|
+
describe '#initialize' do
|
18
|
+
it 'should not create a validation mapping without a behavior block' do
|
19
|
+
expect { Importer::Mapper::ValidationFieldMapping.new('test mapping', ['field1', 'field2', 'field3']) }.
|
20
|
+
to raise_error(Topographer::InvalidMappingError)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
describe '#process_input' do
|
24
|
+
it 'should not return an error if the validation block passes' do
|
25
|
+
validation_mapping.process_input(valid_input, result)
|
26
|
+
expect(result.errors?).to be_false
|
27
|
+
expect(result.data.blank?).to be_true
|
28
|
+
end
|
29
|
+
it 'should return an error if the validation block raises an error' do
|
30
|
+
validation_mapping.process_input(invalid_input, result)
|
31
|
+
expect(result.errors?).to be_true
|
32
|
+
expect(result.errors.values).to include('Sum must be 15')
|
33
|
+
end
|
34
|
+
it 'should not rescue Exceptions that do not inherit from standard error' do
|
35
|
+
mapper = Importer::Mapper::ValidationFieldMapping.new('test mapping', ['field1', 'field2', 'field3']) do |input|
|
36
|
+
sum = input.values.flatten.inject(0) {|sum, x| sum+x}
|
37
|
+
raise Exception, 'Sum must be 15' if sum != 15
|
38
|
+
end
|
39
|
+
expect{ mapper.process_input(invalid_input, result) }.to raise_error(Exception)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Importer::Mapper do
|
4
|
+
describe '.build_mapper' do
|
5
|
+
describe 'required mappings' do
|
6
|
+
it 'can require a one to one field mapping' do
|
7
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
8
|
+
m.required_mapping 'Field1', 'field_1'
|
9
|
+
end
|
10
|
+
mapper.required_columns.should include("Field1")
|
11
|
+
mapper.output_fields.should include('field_1')
|
12
|
+
end
|
13
|
+
it 'can require a many to one field mapping' do
|
14
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
15
|
+
m.required_mapping ['Field1', 'Field2'], 'field_1'
|
16
|
+
end
|
17
|
+
mapper.required_columns.should include("Field1", "Field2")
|
18
|
+
mapper.output_fields.should include('field_1')
|
19
|
+
end
|
20
|
+
it 'cannot require a one to many field mapping' do
|
21
|
+
expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
|
22
|
+
m.required_mapping 'Field1', ['field_1', 'field_2']
|
23
|
+
end
|
24
|
+
}.to raise_error(Topographer::InvalidMappingError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
describe 'optional mappings' do
|
28
|
+
it 'can create an optional one to one field mapping' do
|
29
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
30
|
+
m.optional_mapping 'Field1', 'field_1'
|
31
|
+
end
|
32
|
+
mapper.optional_columns.should include("Field1")
|
33
|
+
mapper.output_fields.should include('field_1')
|
34
|
+
end
|
35
|
+
it 'can create an optional many to one field mapping' do
|
36
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
37
|
+
m.optional_mapping ['Field1', 'Field2'], 'field_1'
|
38
|
+
end
|
39
|
+
mapper.optional_columns.should include("Field1", "Field2")
|
40
|
+
mapper.output_fields.should include('field_1')
|
41
|
+
end
|
42
|
+
it 'cannot create an optional one to many field mapping' do
|
43
|
+
expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
|
44
|
+
m.optional_mapping 'Field1', ['field_1', 'field_2']
|
45
|
+
end
|
46
|
+
}.to raise_error(Topographer::InvalidMappingError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
describe 'ignored mappings' do
|
50
|
+
it 'can ignore a column' do
|
51
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
52
|
+
m.ignored_column 'Field1'
|
53
|
+
end
|
54
|
+
mapper.ignored_columns.should include('Field1')
|
55
|
+
mapper.output_fields.should be_empty
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'raises an error when adding a mapping whose output is already an output of another mapping' do
|
59
|
+
expect { Importer::Mapper.build_mapper(Object) do |m|
|
60
|
+
m.optional_mapping 'Field1', 'field_1'
|
61
|
+
m.required_mapping 'Field2', 'field_1'
|
62
|
+
end }.to raise_error(Topographer::InvalidMappingError)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'raises an error when adding a ignored column, which is already an input of another mapping' do
|
66
|
+
expect { Importer::Mapper.build_mapper(Object) do |m|
|
67
|
+
m.optional_mapping 'Field1', 'field_1'
|
68
|
+
m.ignored_column 'Field1'
|
69
|
+
end }.to raise_error(Topographer::InvalidMappingError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'raises an error when adding a mapped column which has already been ignored' do
|
73
|
+
expect { Importer::Mapper.build_mapper(Object) do |m|
|
74
|
+
m.ignored_column 'Field1'
|
75
|
+
m.optional_mapping 'Field1', 'field_1'
|
76
|
+
end }.to raise_error(Topographer::InvalidMappingError)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'validation mappings' do
|
81
|
+
it 'can create a single column validation' do
|
82
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
83
|
+
m.validation_field 'Field 1 Validation', 'Field1' do |input|
|
84
|
+
raise 'No Input' unless input
|
85
|
+
end
|
86
|
+
end
|
87
|
+
expect(mapper.validation_columns).to include('Field1')
|
88
|
+
expect(mapper.output_fields.empty?).to be_true
|
89
|
+
end
|
90
|
+
it 'can create a multicolumn validation' do
|
91
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
92
|
+
m.validation_field 'Multicolumn Validation', ['Field1', 'Field2'] do |input|
|
93
|
+
raise 'No Input' unless input
|
94
|
+
end
|
95
|
+
end
|
96
|
+
expect(mapper.validation_columns).to eql(['Field1', 'Field2'])
|
97
|
+
expect(mapper.output_fields.empty?).to be_true
|
98
|
+
end
|
99
|
+
it 'raises an error if a validation name is repeated' do
|
100
|
+
expect {
|
101
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
102
|
+
m.validation_field 'Field 1 Validation', 'Field1' do |input|
|
103
|
+
raise 'No Input' unless input
|
104
|
+
end
|
105
|
+
m.validation_field('Field 1 Validation', 'Field1') { |input| raise 'Test Error' }
|
106
|
+
end
|
107
|
+
}.to raise_error( Topographer::InvalidMappingError )
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'static mappings' do
|
112
|
+
it 'can create a static mapping' do
|
113
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
114
|
+
m.default_value 'Field1' do
|
115
|
+
34
|
116
|
+
end
|
117
|
+
end
|
118
|
+
expect(mapper.default_fields).to eql(['Field1'])
|
119
|
+
expect(mapper.output_fields).to eql(['Field1'])
|
120
|
+
end
|
121
|
+
it 'cannot create a static mapping to many columns' do
|
122
|
+
expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
|
123
|
+
m.default_value ['Field1', 'Field2'] do
|
124
|
+
34
|
125
|
+
end
|
126
|
+
end }.to raise_error(Topographer::InvalidMappingError)
|
127
|
+
end
|
128
|
+
it 'cannot add a static mapping to a field that has already been mapped' do
|
129
|
+
expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
|
130
|
+
m.required_mapping 'Field1', 'field1'
|
131
|
+
m.default_value 'field1' do
|
132
|
+
34
|
133
|
+
end
|
134
|
+
end }.to raise_error(Topographer::InvalidMappingError)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'associates the model class with the mapper instance' do
|
139
|
+
mapper = Importer::Mapper.build_mapper(Object) do |m|
|
140
|
+
m.ignored_column 'Field1'
|
141
|
+
m.optional_mapping 'Field2', 'field_1'
|
142
|
+
end
|
143
|
+
expect(mapper.model_class).to be Object
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '#key_field' do
|
148
|
+
let(:mapper) { Importer::Mapper.new(Object) }
|
149
|
+
it 'should add a key field to the list of key fields' do
|
150
|
+
mapper.key_field 'Field1'
|
151
|
+
expect(mapper.key_fields).to eql(['Field1'])
|
152
|
+
end
|
153
|
+
it 'should not allow multiple key fields at one time' do
|
154
|
+
expect {
|
155
|
+
mapper.key_field ['Field1', 'Field2']
|
156
|
+
}.to raise_error(Topographer::InvalidMappingError)
|
157
|
+
end
|
158
|
+
it 'should not allow the same key field more than once' do
|
159
|
+
expect {
|
160
|
+
mapper.key_field 'Field1'
|
161
|
+
mapper.key_field 'Field1'
|
162
|
+
}.to raise_error(Topographer::InvalidMappingError)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#input_structure_valid?' do
|
167
|
+
let(:mapper) do
|
168
|
+
Importer::Mapper.build_mapper(Object) do |m|
|
169
|
+
m.required_mapping 'Field1', 'field1'
|
170
|
+
m.required_mapping 'Field2', 'field_4'
|
171
|
+
m.optional_mapping 'Field3', 'field_6'
|
172
|
+
m.validation_field('test validation', 'Field4') { |input| raise 'FAILURE' if !input }
|
173
|
+
m.ignored_column 'Field5'
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
let(:valid_input_structure_with_options) { ['Field1', 'Field2', 'Field3', 'Field4'] }
|
178
|
+
let(:valid_input_structure_without_options) { ['Field1', 'Field2', 'Field4'] }
|
179
|
+
let(:missing_required_column_structure) { ['Field1', 'Field3'] }
|
180
|
+
let(:missing_validation_column_structure) { ['Field1', 'Field2', 'Field3'] }
|
181
|
+
let(:bad_column_structure) { ['Field1', 'UnknownField', 'Field2', 'Field4'] }
|
182
|
+
|
183
|
+
it 'returns false if required fields are missing' do
|
184
|
+
expect(mapper.input_structure_valid?(missing_required_column_structure)).to be_false
|
185
|
+
end
|
186
|
+
it 'returns false if a validation field is missing' do
|
187
|
+
expect(mapper.input_structure_valid?(missing_validation_column_structure)).to be_false
|
188
|
+
end
|
189
|
+
it 'returns true if all of the required fields are present' do
|
190
|
+
expect(mapper.input_structure_valid?(valid_input_structure_without_options)).to be_true
|
191
|
+
end
|
192
|
+
it 'returns true if all the required and optional fields are present' do
|
193
|
+
expect(mapper.input_structure_valid?(valid_input_structure_with_options)).to be_true
|
194
|
+
end
|
195
|
+
it 'returns true regardless of whether ignored fields are present' do
|
196
|
+
expect(mapper.input_structure_valid?(valid_input_structure_with_options)).to be_true
|
197
|
+
expect(mapper.input_structure_valid?(valid_input_structure_with_options+['Field5'])).to be_true
|
198
|
+
end
|
199
|
+
it 'returns false if there are any extra fields that have not been ignored' do
|
200
|
+
mapper.input_structure_valid?(bad_column_structure).should be_false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '#bad_columns' do
|
205
|
+
let(:mapper) do
|
206
|
+
Importer::Mapper.build_mapper(Object) do |m|
|
207
|
+
m.required_mapping 'Field1', 'field1'
|
208
|
+
m.required_mapping 'Field2', 'field_4'
|
209
|
+
m.validation_field('test validation 2', 'Field2') { |input| raise 'FAILURE' if !input }
|
210
|
+
m.optional_mapping 'Field3', 'field_6'
|
211
|
+
m.validation_field('test validation', 'Field4') { |input| raise 'FAILURE' if !input }
|
212
|
+
m.ignored_column 'Field5'
|
213
|
+
end
|
214
|
+
end
|
215
|
+
let(:bad_column_structure) { ['Field1', 'Bad Field', 'Field3'] }
|
216
|
+
|
217
|
+
it 'should return bad column names' do
|
218
|
+
mapper.input_structure_valid?(bad_column_structure)
|
219
|
+
expect(mapper.bad_columns).to eql(['Bad Field'])
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe '#missing_columns' do
|
224
|
+
let(:mapper) do
|
225
|
+
Importer::Mapper.build_mapper(Object) do |m|
|
226
|
+
m.required_mapping 'Field1', 'field1'
|
227
|
+
m.required_mapping 'Field2', 'field_4'
|
228
|
+
m.optional_mapping 'Field3', 'field_6'
|
229
|
+
m.validation_field('test validation', 'Field4') { |input| raise 'FAILURE' if !input }
|
230
|
+
m.ignored_column 'Field5'
|
231
|
+
end
|
232
|
+
end
|
233
|
+
let(:missing_column_structure) { ['Field1'] }
|
234
|
+
|
235
|
+
it 'should return missing column names' do
|
236
|
+
mapper.input_structure_valid?(missing_column_structure)
|
237
|
+
expect(mapper.missing_columns).to eql(['Field2', 'Field4'])
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe '#map_input' do
|
242
|
+
let(:mapper) do
|
243
|
+
Importer::Mapper.build_mapper(Object) do |m|
|
244
|
+
m.required_mapping 'Field1', 'field_1'
|
245
|
+
m.required_mapping ['Field1', 'Field2'], 'field_2'
|
246
|
+
m.validation_field('Field2 Validation', 'Field2') { |input| raise 'FAILURE' if input['Field2'] != 'datum2'}
|
247
|
+
m.required_mapping('Field1', 'field_3') { |x| x['Field1'].length }
|
248
|
+
m.required_mapping(['Field1', 'Field3'], 'field_4') { |x| x['Field1'].length * x['Field3'] }
|
249
|
+
m.default_value('static_field') { 34 }
|
250
|
+
m.optional_mapping('Field4', 'field_5')
|
251
|
+
end
|
252
|
+
end
|
253
|
+
let(:valid_input) do
|
254
|
+
double 'SourceData',
|
255
|
+
source_identifier: 'row1',
|
256
|
+
data: {'Field1' => 'datum1',
|
257
|
+
'Field2' => 'datum2',
|
258
|
+
'Field3' => 6}
|
259
|
+
end
|
260
|
+
|
261
|
+
let(:invalid_data_input) do
|
262
|
+
double 'SourceData',
|
263
|
+
source_identifier: 'row1',
|
264
|
+
data: {'Field1' => 'datum1',
|
265
|
+
'Field2' => 'bad_field2_data',
|
266
|
+
'Field3' => 6}
|
267
|
+
end
|
268
|
+
|
269
|
+
let(:missing_field_input) do
|
270
|
+
double 'SourceData',
|
271
|
+
source_identifier: 'row1',
|
272
|
+
data: {'Field1' => 'datum1',
|
273
|
+
'Field2' => 'datum2'}
|
274
|
+
end
|
275
|
+
|
276
|
+
let(:result) do
|
277
|
+
mapper.map_input(valid_input)
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
it 'maps one to one field information' do
|
282
|
+
expect(result.data['field_1']).to eql('datum1')
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'maps many to one field information' do
|
286
|
+
expect(result.data['field_2']).to eql('datum1, datum2')
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'maps one to one field information with complex behavior' do
|
290
|
+
expect(result.data['field_3']).to eql(6)
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'maps many to one field information with complex behavior' do
|
294
|
+
expect(result.data['field_4']).to eql(36)
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'maps static fields' do
|
298
|
+
expect(result.data['static_field']).to eql(34)
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'returns an error if required field is missing in input data' do
|
302
|
+
invalid_field_result = mapper.map_input(missing_field_input)
|
303
|
+
expect(invalid_field_result.errors?).to be_true
|
304
|
+
expect(invalid_field_result.errors['field_4']).to include('Missing required input(s): `Field3` for `field_4`')
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'returns an error if a validation does not pass' do
|
308
|
+
invalid_data_result = mapper.map_input(invalid_data_input)
|
309
|
+
expect(invalid_data_result.errors?).to be_true
|
310
|
+
expect(invalid_data_result.errors['Field2 Validation']).to include('FAILURE')
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'does not return an error if an optional field is missing in the input data' do
|
315
|
+
expect(result.errors?).to be_false
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|