atlas_engine 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -24
  3. data/app/countries/atlas_engine/at/country_profile.yml +1 -1
  4. data/app/countries/atlas_engine/au/country_profile.yml +1 -1
  5. data/app/countries/atlas_engine/be/country_profile.yml +2 -2
  6. data/app/countries/atlas_engine/ch/country_profile.yml +1 -1
  7. data/app/countries/atlas_engine/fr/country_profile.yml +1 -1
  8. data/app/countries/atlas_engine/nl/country_profile.yml +1 -1
  9. data/app/countries/atlas_engine/pl/country_profile.yml +1 -1
  10. data/app/countries/atlas_engine/us/country_profile.yml +1 -0
  11. data/app/graphql/atlas_engine/schema.graphql +14 -1
  12. data/app/graphql/atlas_engine/types/address_validation/address_input.rb +4 -0
  13. data/app/graphql/atlas_engine/types/address_validation/concern_type.rb +8 -1
  14. data/app/graphql/atlas_engine/types/address_validation/suggestion_type.rb +4 -0
  15. data/app/graphql/atlas_engine/types/message_format_type.rb +11 -0
  16. data/app/graphql/atlas_engine/types/query_type.rb +3 -1
  17. data/app/lib/atlas_engine/validation_transcriber/address_parsings.rb +14 -9
  18. data/app/lib/atlas_engine/validation_transcriber/formatter.rb +10 -1
  19. data/app/models/atlas_engine/address_number_range.rb +1 -1
  20. data/app/models/atlas_engine/address_validation/abstract_address.rb +12 -0
  21. data/app/models/atlas_engine/address_validation/address.rb +8 -0
  22. data/app/models/atlas_engine/address_validation/concern_record.rb +28 -0
  23. data/app/models/atlas_engine/address_validation/es/validators/full_address.rb +18 -5
  24. data/app/models/atlas_engine/address_validation/es/validators/full_address_street.rb +2 -2
  25. data/app/models/atlas_engine/address_validation/full_address_validator_base.rb +6 -2
  26. data/app/models/atlas_engine/address_validation/message_format.rb +13 -0
  27. data/app/models/atlas_engine/address_validation/predicate_pipeline_builder.rb +90 -0
  28. data/app/models/atlas_engine/address_validation/request.rb +1 -0
  29. data/app/models/atlas_engine/address_validation/suggestion.rb +26 -1
  30. data/app/models/atlas_engine/address_validation/validator.rb +31 -14
  31. data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result.rb +9 -5
  32. data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result_base.rb +4 -3
  33. data/app/models/atlas_engine/address_validation/validators/full_address/concern_builder.rb +19 -4
  34. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_concern_builder.rb +4 -3
  35. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_for_country_concern_builder.rb +6 -4
  36. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_for_province_concern_builder.rb +4 -2
  37. data/app/models/atlas_engine/address_validation/validators/full_address/no_candidate_result.rb +1 -1
  38. data/app/models/atlas_engine/address_validation/validators/full_address/suggestion_builder.rb +9 -2
  39. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_address_concern_builder.rb +16 -4
  40. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_province_concern_builder.rb +8 -3
  41. data/app/models/atlas_engine/address_validation/validators/full_address/unmatched_field_concern_builder.rb +13 -3
  42. data/app/models/atlas_engine/address_validation/validators/full_address/unsupported_script_result.rb +1 -1
  43. data/app/models/atlas_engine/address_validation/validators/predicates/city/present.rb +1 -1
  44. data/app/models/atlas_engine/address_validation/validators/predicates/country/exists.rb +5 -1
  45. data/app/models/atlas_engine/address_validation/validators/predicates/country/valid_for_zip.rb +1 -1
  46. data/app/models/atlas_engine/address_validation/validators/predicates/neighborhood/present.rb +35 -0
  47. data/app/models/atlas_engine/address_validation/validators/predicates/not_exceed_max_length.rb +18 -3
  48. data/app/models/atlas_engine/address_validation/validators/predicates/phone/valid.rb +3 -1
  49. data/app/models/atlas_engine/address_validation/validators/predicates/predicate.rb +6 -2
  50. data/app/models/atlas_engine/address_validation/validators/predicates/province/exists.rb +3 -1
  51. data/app/models/atlas_engine/address_validation/validators/predicates/province/valid_for_country.rb +3 -1
  52. data/app/models/atlas_engine/address_validation/validators/predicates/street/present.rb +3 -1
  53. data/app/models/atlas_engine/address_validation/validators/predicates/street_name/present.rb +35 -0
  54. data/app/models/atlas_engine/address_validation/validators/predicates/street_number/present.rb +35 -0
  55. data/app/models/atlas_engine/address_validation/validators/predicates/zip/present.rb +2 -2
  56. data/app/models/atlas_engine/address_validation/validators/predicates/zip/valid_for_country.rb +1 -1
  57. data/app/models/atlas_engine/address_validation/validators/predicates/zip/valid_for_province.rb +1 -1
  58. data/app/models/atlas_engine/services/validation.rb +4 -0
  59. data/db/data/validation_pipelines/es.yml +46 -0
  60. data/db/data/validation_pipelines/es_street.yml +46 -0
  61. data/db/data/validation_pipelines/local.yml +46 -0
  62. data/lib/atlas_engine/version.rb +1 -1
  63. metadata +39 -5
@@ -0,0 +1,90 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class PredicatePipelineBuilder
7
+ extend T::Sig
8
+
9
+ sig { returns(PredicatePipeline) }
10
+ attr_reader :predicate_pipeline
11
+
12
+ sig { returns(AbstractAddress) }
13
+ attr_reader :address
14
+
15
+ sig { returns(Result) }
16
+ attr_reader :result
17
+
18
+ sig { returns(MessageFormat) }
19
+ attr_reader :message_format
20
+
21
+ sig do
22
+ params(
23
+ matching_strategy_name: String,
24
+ address: AbstractAddress,
25
+ result: Result,
26
+ message_format: MessageFormat,
27
+ ).void
28
+ end
29
+ def initialize(matching_strategy_name:, address:, result:, message_format:)
30
+ @address = address
31
+ @predicate_pipeline = T.let(PredicatePipeline.find(matching_strategy_name), PredicatePipeline)
32
+ @result = result
33
+ @message_format = message_format
34
+ end
35
+
36
+ sig { returns(T::Array[PredicatePipeline::PredicateConfig]) }
37
+ def pipeline
38
+ predicate_pipeline.pipeline.reject do |predicate_config|
39
+ unsupported_fields.include?(predicate_config.field)
40
+ end
41
+ end
42
+
43
+ sig { returns(T.nilable(FullAddressValidatorBase)) }
44
+ def full_address_validator
45
+ predicate_pipeline.full_address_validator&.new(
46
+ address: address,
47
+ result: result,
48
+ message_format: message_format,
49
+ )
50
+ end
51
+
52
+ sig do
53
+ returns(T::Array[Symbol])
54
+ end
55
+ def unsupported_fields
56
+ @unsupported_fields ||= T.let(
57
+ begin
58
+ unsupported_fields = []
59
+ unsupported_fields += address1_format == :extended ? [:address1] : [:street_name, :street_number]
60
+ unsupported_fields += address2_format == :extended ? [:address2] : [:line2, :neighborhood]
61
+ unsupported_fields
62
+ end,
63
+ T.nilable(T::Array[T.untyped]),
64
+ )
65
+ end
66
+
67
+ private
68
+
69
+ sig { returns(Symbol) }
70
+ def address1_format
71
+ T.let(
72
+ [:street_name, :street_number].all? do |field|
73
+ address.send(field).nil?
74
+ end ? :standard : :extended,
75
+ Symbol,
76
+ )
77
+ end
78
+
79
+ sig { returns(Symbol) }
80
+ def address2_format
81
+ T.let(
82
+ [:neighborhood, :line2].all? do |field|
83
+ address.send(field).nil?
84
+ end ? :standard : :extended,
85
+ Symbol,
86
+ )
87
+ end
88
+ end
89
+ end
90
+ end
@@ -7,6 +7,7 @@ module AtlasEngine
7
7
  const :address, AbstractAddress
8
8
  const :locale, String, default: "en"
9
9
  const :matching_strategy, T.nilable(T.any(String, Symbol)), default: nil
10
+ const :message_format, T.nilable(T.any(String, Symbol)), default: nil
10
11
  end
11
12
  end
12
13
  end
@@ -12,9 +12,21 @@ module AtlasEngine
12
12
  sig { returns(T.nilable(String)) }
13
13
  attr_accessor :address1
14
14
 
15
+ sig { returns(T.nilable(String)) }
16
+ attr_accessor :street_name
17
+
18
+ sig { returns(T.nilable(String)) }
19
+ attr_accessor :street_number
20
+
15
21
  sig { returns(T.nilable(String)) }
16
22
  attr_accessor :address2
17
23
 
24
+ sig { returns(T.nilable(String)) }
25
+ attr_accessor :line2
26
+
27
+ sig { returns(T.nilable(String)) }
28
+ attr_accessor :neighborhood
29
+
18
30
  sig { returns(T.nilable(String)) }
19
31
  attr_accessor :city
20
32
 
@@ -30,16 +42,25 @@ module AtlasEngine
30
42
  sig do
31
43
  params(
32
44
  address1: T.nilable(String),
45
+ street_name: T.nilable(String),
46
+ street_number: T.nilable(String),
33
47
  address2: T.nilable(String),
48
+ line2: T.nilable(String),
49
+ neighborhood: T.nilable(String),
34
50
  city: T.nilable(String),
35
51
  zip: T.nilable(String),
36
52
  province_code: T.nilable(String),
37
53
  country_code: T.nilable(String),
38
54
  ).void
39
55
  end
40
- def initialize(address1: nil, address2: nil, city: nil, zip: nil, province_code: nil, country_code: nil)
56
+ def initialize(address1: nil, street_name: nil, street_number: nil, address2: nil, line2: nil, neighborhood: nil,
57
+ city: nil, zip: nil, province_code: nil, country_code: nil)
41
58
  @address1 = address1
59
+ @street_name = street_name
60
+ @street_number = street_number
42
61
  @address2 = address2
62
+ @line2 = line2
63
+ @neighborhood = neighborhood
43
64
  @city = city
44
65
  @zip = zip
45
66
  @province_code = province_code
@@ -55,7 +76,11 @@ module AtlasEngine
55
76
  {
56
77
  id: id,
57
78
  address1: address1,
79
+ street_name: street_name,
80
+ street_number: street_number,
58
81
  address2: address2,
82
+ line2: line2,
83
+ neighborhood: neighborhood,
59
84
  city: city,
60
85
  zip: zip,
61
86
  province_code: province_code,
@@ -28,6 +28,12 @@ module AtlasEngine
28
28
  sig { returns(T.nilable(FullAddressValidatorBase)) }
29
29
  attr_reader :full_address_validator
30
30
 
31
+ sig { returns(T::Array[Symbol]) }
32
+ attr_reader :unsupported_fields
33
+
34
+ sig { returns(MessageFormat) }
35
+ attr_reader :message_format
36
+
31
37
  FIELD_MAP = T.let(
32
38
  {
33
39
  country: "country_code",
@@ -36,6 +42,10 @@ module AtlasEngine
36
42
  city: "city",
37
43
  address1: "address1",
38
44
  address2: "address2",
45
+ street_name: "street_name",
46
+ street_number: "street_number",
47
+ neighborhood: "neighborhood",
48
+ line2: "line2",
39
49
  phone: "phone",
40
50
  },
41
51
  T::Hash[Symbol, String],
@@ -47,13 +57,15 @@ module AtlasEngine
47
57
  matching_strategy: Strategies,
48
58
  locale: String,
49
59
  context: T::Hash[T.untyped, T.untyped],
60
+ message_format: MessageFormat,
50
61
  ).void
51
62
  end
52
63
  def initialize(
53
64
  address:,
54
65
  matching_strategy:,
55
66
  locale: "en",
56
- context: {}
67
+ context: {},
68
+ message_format: MessageFormat::Instructional
57
69
  )
58
70
  @address = T.let(address, AbstractAddress)
59
71
  @address1 = T.let(address.address1, T.nilable(String))
@@ -65,9 +77,9 @@ module AtlasEngine
65
77
  @country_code = T.let(address.country_code, T.nilable(String))
66
78
  @context = T.let(context, T::Hash[T.untyped, T.untyped])
67
79
  @matching_strategy = T.let(matching_strategy, Strategies)
80
+ @message_format = T.let(message_format, MessageFormat)
68
81
 
69
82
  matching_strategy_name = matching_strategy.serialize
70
- @predicate_pipeline = T.let(PredicatePipeline.find(matching_strategy_name), PredicatePipeline)
71
83
 
72
84
  @result = T.let(
73
85
  Result.new(
@@ -78,14 +90,14 @@ module AtlasEngine
78
90
  ),
79
91
  Result,
80
92
  )
93
+ pipeline_builder = PredicatePipelineBuilder.new(matching_strategy_name:, address:, result:, message_format:)
81
94
 
82
- @full_address_validator = T.let(
83
- @predicate_pipeline.full_address_validator&.new(
84
- address: @address,
85
- result: @result,
86
- ),
87
- T.nilable(FullAddressValidatorBase),
95
+ @pipeline = T.let(
96
+ pipeline_builder.pipeline,
97
+ T::Array[AtlasEngine::AddressValidation::PredicatePipeline::PredicateConfig],
88
98
  )
99
+ @unsupported_fields = T.let(pipeline_builder.unsupported_fields, T::Array[Symbol])
100
+ @full_address_validator = T.let(pipeline_builder.full_address_validator, T.nilable(FullAddressValidatorBase))
89
101
  end
90
102
 
91
103
  sig { override.returns(Result) }
@@ -113,15 +125,20 @@ module AtlasEngine
113
125
  pipeline_address = Address.from_address(address: address)
114
126
  local_concerns = {}
115
127
  cache = Validators::Predicates::Cache.new(pipeline_address)
116
- @predicate_pipeline.pipeline.each do |config|
128
+ @pipeline.each do |predicate_config|
117
129
  break if local_concerns[:country].present?
118
130
 
119
- local_concerns[config.field] = [] if local_concerns[config.field].nil?
120
- next if local_concerns[config.field].present?
131
+ local_concerns[predicate_config.field] = [] if local_concerns[predicate_config.field].nil?
132
+ next if local_concerns[predicate_config.field].present?
121
133
 
122
- concern = config.class_name.new(field: config.field, address: pipeline_address, cache: cache).evaluate
134
+ concern = predicate_config.class_name.new(
135
+ field: predicate_config.field,
136
+ address: pipeline_address,
137
+ cache: cache,
138
+ message_format: message_format,
139
+ ).evaluate
123
140
 
124
- local_concerns[config.field] << concern if concern.present?
141
+ local_concerns[predicate_config.field] << concern if concern.present?
125
142
  end
126
143
  local_concerns
127
144
  end
@@ -129,7 +146,7 @@ module AtlasEngine
129
146
  sig { params(local_concerns: T::Hash[Symbol, T::Array[Concern]]).void }
130
147
  def populate_result(local_concerns)
131
148
  local_concerns.keys.each do |field|
132
- if local_concerns[field]&.empty? && [:address2, :phone].exclude?(field)
149
+ if local_concerns[field]&.empty? && [:address2, :phone].concat(unsupported_fields).exclude?(field)
133
150
  result.validation_scope << T.must(FIELD_MAP[field])
134
151
  end
135
152
 
@@ -14,10 +14,11 @@ module AtlasEngine
14
14
  address_comparison: AddressComparison,
15
15
  matching_strategy: AddressValidation::MatchingStrategies,
16
16
  result: Result,
17
+ message_format: MessageFormat,
17
18
  ).void
18
19
  end
19
- def initialize(address_comparison:, matching_strategy:, result:)
20
- super(address: address_comparison.address, result: result)
20
+ def initialize(address_comparison:, matching_strategy:, result:, message_format:)
21
+ super(address: address_comparison.address, result: result, message_format: message_format)
21
22
  @address_comparison = address_comparison
22
23
  @candidate = address_comparison.candidate
23
24
  @matching_strategy = matching_strategy
@@ -56,11 +57,11 @@ module AtlasEngine
56
57
 
57
58
  sig { void }
58
59
  def add_concerns_without_suggestions
59
- concern = InvalidZipConcernBuilder.for(address, [])
60
+ concern = InvalidZipConcernBuilder.for(address, [], message_format)
60
61
  result.concerns << concern if concern
61
62
 
62
63
  if ConcernBuilder.too_many_unmatched_components?(address, unmatched_components.keys)
63
- result.concerns << UnknownAddressConcernBuilder.new(address).build
64
+ result.concerns << UnknownAddressConcernBuilder.new(address, message_format).build
64
65
  end
65
66
  end
66
67
 
@@ -79,6 +80,7 @@ module AtlasEngine
79
80
  matched_components: matched_components_to_validate.keys,
80
81
  address: address,
81
82
  suggestion_ids: [suggestion.id].compact,
83
+ message_format: message_format,
82
84
  ).build
83
85
  result.concerns << concern
84
86
  end
@@ -167,7 +169,9 @@ module AtlasEngine
167
169
 
168
170
  original_street = T.must(unmatched_components_to_validate[:street]).left_sequence.raw_value
169
171
 
170
- if address.address1.to_s.include?(original_street)
172
+ if address.street_name.present?
173
+ :street_name
174
+ elsif address.address1.to_s.include?(original_street)
171
175
  :address1
172
176
  elsif address.address2.to_s.include?(original_street)
173
177
  :address2
@@ -11,10 +11,11 @@ module AtlasEngine
11
11
 
12
12
  abstract!
13
13
 
14
- sig { params(address: AbstractAddress, result: Result).void }
15
- def initialize(address:, result:)
14
+ sig { params(address: AbstractAddress, result: Result, message_format: MessageFormat).void }
15
+ def initialize(address:, result:, message_format: MessageFormat::Instructional)
16
16
  @address = address
17
17
  @result = result
18
+ @message_format = message_format
18
19
  end
19
20
 
20
21
  sig { void }
@@ -22,7 +23,7 @@ module AtlasEngine
22
23
 
23
24
  private
24
25
 
25
- attr_reader :result, :address
26
+ attr_reader :result, :address, :message_format
26
27
 
27
28
  sig { void }
28
29
  def update_result_scope
@@ -8,7 +8,12 @@ module AtlasEngine
8
8
  class ConcernBuilder
9
9
  extend T::Sig
10
10
 
11
- attr_reader :unmatched_component, :unmatched_field, :matched_components, :address, :suggestion_ids
11
+ attr_reader :unmatched_component,
12
+ :unmatched_field,
13
+ :matched_components,
14
+ :address,
15
+ :suggestion_ids,
16
+ :message_format
12
17
 
13
18
  class << self
14
19
  extend T::Sig
@@ -79,15 +84,24 @@ module AtlasEngine
79
84
  matched_components: T::Array[Symbol],
80
85
  address: AbstractAddress,
81
86
  suggestion_ids: T::Array[String],
87
+ message_format: MessageFormat,
82
88
  unmatched_field: T.nilable(Symbol),
83
89
  ).void
84
90
  end
85
- def initialize(unmatched_component:, matched_components:, address:, suggestion_ids:, unmatched_field: nil)
91
+ def initialize(
92
+ unmatched_component:,
93
+ matched_components:,
94
+ address:,
95
+ suggestion_ids:,
96
+ message_format:,
97
+ unmatched_field: nil
98
+ )
86
99
  @unmatched_component = unmatched_component
87
100
  @unmatched_field = unmatched_field
88
101
  @matched_components = matched_components
89
102
  @address = address
90
103
  @suggestion_ids = suggestion_ids
104
+ @message_format = message_format
91
105
  end
92
106
 
93
107
  sig { returns(AddressValidation::Concern) }
@@ -106,7 +120,7 @@ module AtlasEngine
106
120
 
107
121
  sig { returns(AddressValidation::Concern) }
108
122
  def build_zip_concern
109
- concern = InvalidZipConcernBuilder.for(address, suggestion_ids)
123
+ concern = InvalidZipConcernBuilder.for(address, suggestion_ids, message_format)
110
124
  return concern if concern
111
125
 
112
126
  build_default_concern
@@ -115,7 +129,7 @@ module AtlasEngine
115
129
  sig { returns(AddressValidation::Concern) }
116
130
  def build_province_concern
117
131
  if ([:zip, :city] - matched_components).empty?
118
- UnknownProvinceConcernBuilder.new(address).build(suggestion_ids)
132
+ UnknownProvinceConcernBuilder.new(address, message_format).build(suggestion_ids)
119
133
  else
120
134
  build_default_concern
121
135
  end
@@ -128,6 +142,7 @@ module AtlasEngine
128
142
  matched_components,
129
143
  address,
130
144
  unmatched_field,
145
+ message_format,
131
146
  ).build(suggestion_ids)
132
147
  end
133
148
  end
@@ -13,9 +13,10 @@ module AtlasEngine
13
13
  params(
14
14
  address: AbstractAddress,
15
15
  suggestion_ids: T::Array[String],
16
+ message_format: MessageFormat,
16
17
  ).returns(T.nilable(AddressValidation::Concern))
17
18
  end
18
- def for(address, suggestion_ids)
19
+ def for(address, suggestion_ids, message_format)
19
20
  country = Worldwide.region(code: address.country_code)
20
21
 
21
22
  province = country.zone(code: address.province_code.presence || "")
@@ -24,11 +25,11 @@ module AtlasEngine
24
25
  if country_expects_zone_in_address?(country) && province.province?
25
26
  return if province.valid_zip?(address.zip)
26
27
 
27
- InvalidZipForProvinceConcernBuilder.new(address).build
28
+ InvalidZipForProvinceConcernBuilder.new(address, message_format).build
28
29
  else
29
30
  return if country.valid_zip?(address.zip)
30
31
 
31
- InvalidZipForCountryConcernBuilder.new(address).build(suggestion_ids)
32
+ InvalidZipForCountryConcernBuilder.new(address, message_format).build(suggestion_ids)
32
33
  end
33
34
  end
34
35
 
@@ -9,16 +9,18 @@ module AtlasEngine
9
9
  extend T::Sig
10
10
  include ConcernFormatter
11
11
  attr_reader :address
12
+ attr_reader :message_format
12
13
 
13
- sig { params(address: AbstractAddress).void }
14
- def initialize(address)
14
+ sig { params(address: AbstractAddress, message_format: MessageFormat).void }
15
+ def initialize(address, message_format)
15
16
  @address = address
17
+ @message_format = message_format
16
18
  end
17
19
 
18
20
  sig { params(suggestion_ids: T::Array[String]).returns(Concern) }
19
21
  def build(suggestion_ids = [])
20
- message = country.field(key: :zip).error(
21
- code: :invalid_for_country,
22
+ message = country.field(key: :zip).error(
23
+ code: "invalid_for_country_#{message_format.serialize}".to_sym,
22
24
  options: { country: country.full_name },
23
25
  ).to_s
24
26
 
@@ -9,15 +9,17 @@ module AtlasEngine
9
9
  extend T::Sig
10
10
  include ConcernFormatter
11
11
  attr_reader :address
12
+ attr_reader :message_format
12
13
 
13
- def initialize(address)
14
+ def initialize(address, message_format)
14
15
  @address = address
16
+ @message_format = message_format
15
17
  end
16
18
 
17
19
  sig { params(suggestion_ids: T::Array[String]).returns(Concern) }
18
20
  def build(suggestion_ids = [])
19
21
  message = country.field(key: :zip).error(
20
- code: :invalid_for_province,
22
+ code: "invalid_for_province_#{message_format.serialize}".to_sym,
21
23
  options: { province: province_name },
22
24
  ).to_s
23
25
 
@@ -12,7 +12,7 @@ module AtlasEngine
12
12
  def update_result
13
13
  result.concerns << UnknownAddressConcernBuilder.new(address).build
14
14
 
15
- concern = InvalidZipConcernBuilder.for(address, [])
15
+ concern = InvalidZipConcernBuilder.for(address, [], message_format)
16
16
  result.concerns << concern if concern
17
17
 
18
18
  update_result_scope
@@ -12,7 +12,11 @@ module AtlasEngine
12
12
  params(
13
13
  address: {
14
14
  address1: T.nilable(String),
15
+ street_name: T.nilable(String),
16
+ street_number: T.nilable(String),
15
17
  address2: T.nilable(String),
18
+ line2: T.nilable(String),
19
+ neighborhood: T.nilable(String),
16
20
  city: T.nilable(String),
17
21
  province_code: T.nilable(String),
18
22
  country_code: T.nilable(String),
@@ -70,9 +74,12 @@ module AtlasEngine
70
74
  original_street = comparison.left_sequence.raw_value
71
75
  field = unmatched_fields[:street]
72
76
 
73
- if field == :address1
77
+ case field
78
+ when :street_name
79
+ suggestion.street_name = suggested_street
80
+ when :address1
74
81
  suggestion.address1 = suggestion.address1.to_s.sub(original_street, suggested_street)
75
- elsif field == :address2
82
+ when :address2
76
83
  suggestion.address2 = suggestion.address2.to_s.sub(original_street, suggested_street)
77
84
  end
78
85
 
@@ -12,14 +12,20 @@ module AtlasEngine
12
12
  sig { returns(TAddress) }
13
13
  attr_reader :address
14
14
 
15
- sig { params(address: TAddress).void }
16
- def initialize(address)
15
+ sig { returns(MessageFormat) }
16
+ attr_reader :message_format
17
+
18
+ sig { params(address: TAddress, message_format: MessageFormat).void }
19
+ def initialize(address, message_format = MessageFormat::Instructional)
17
20
  @address = address
21
+ @message_format = message_format
18
22
  end
19
23
 
20
24
  sig { params(suggestion_ids: T::Array[String]).returns(Concern) }
21
25
  def build(suggestion_ids = [])
22
- message = country.field(key: :address).error(code: :may_not_exist)
26
+ message = country.field(key: :address).error(
27
+ code: "may_not_exist_#{message_format.serialize}".to_sym,
28
+ ).to_s
23
29
 
24
30
  Concern.new(
25
31
  code: :address_unknown,
@@ -27,9 +33,15 @@ module AtlasEngine
27
33
  type: T.must(Concern::TYPES[:warning]),
28
34
  type_level: 1,
29
35
  suggestion_ids: suggestion_ids,
30
- field_names: [:address1],
36
+ field_names: [field],
31
37
  )
32
38
  end
39
+
40
+ private
41
+
42
+ def field
43
+ address.street_name.present? ? :street_name : :address1
44
+ end
33
45
  end
34
46
  end
35
47
  end
@@ -8,17 +8,22 @@ module AtlasEngine
8
8
  class UnknownProvinceConcernBuilder
9
9
  extend T::Sig
10
10
  include ConcernFormatter
11
+
11
12
  attr_reader :address
12
13
 
13
- sig { params(address: AbstractAddress).void }
14
- def initialize(address)
14
+ sig { returns(MessageFormat) }
15
+ attr_reader :message_format
16
+
17
+ sig { params(address: AbstractAddress, message_format: MessageFormat).void }
18
+ def initialize(address, message_format = MessageFormat::Instructional)
15
19
  @address = address
20
+ @message_format = message_format
16
21
  end
17
22
 
18
23
  sig { params(suggestion_ids: T::Array[String]).returns(Concern) }
19
24
  def build(suggestion_ids = [])
20
25
  message = country.field(key: :province).error(
21
- code: :unknown_for_city_and_zip,
26
+ code: "unknown_for_city_and_zip_#{message_format.serialize}".to_sym,
22
27
  options: { city: address.city, zip: address.zip },
23
28
  ).to_s
24
29
 
@@ -8,7 +8,7 @@ module AtlasEngine
8
8
  class UnmatchedFieldConcernBuilder
9
9
  extend T::Sig
10
10
  include ConcernFormatter
11
- attr_reader :address, :unmatched_component, :matched_components, :unmatched_field
11
+ attr_reader :address, :unmatched_component, :matched_components, :unmatched_field, :message_format
12
12
 
13
13
  COMPONENTS_TO_LABELS = {
14
14
  zip: "ZIP",
@@ -27,13 +27,21 @@ module AtlasEngine
27
27
  matched_components: T::Array[Symbol],
28
28
  address: AbstractAddress,
29
29
  unmatched_field: T.nilable(Symbol),
30
+ message_format: MessageFormat,
30
31
  ).void
31
32
  end
32
- def initialize(unmatched_component, matched_components, address, unmatched_field = nil)
33
+ def initialize(
34
+ unmatched_component,
35
+ matched_components,
36
+ address,
37
+ unmatched_field = nil,
38
+ message_format = MessageFormat::Instructional
39
+ )
33
40
  @address = address
34
41
  @unmatched_component = unmatched_component
35
42
  @matched_components = matched_components
36
43
  @unmatched_field = unmatched_field || unmatched_component
44
+ @message_format = message_format
37
45
  end
38
46
 
39
47
  sig do
@@ -56,7 +64,9 @@ module AtlasEngine
56
64
 
57
65
  sig { returns(String) }
58
66
  def message
59
- country.field(key: field_name).error(code: :unknown_for_address).to_s
67
+ country.field(key: field_name).error(
68
+ code: "unknown_for_address_#{message_format.serialize}".to_sym,
69
+ ).to_s
60
70
  end
61
71
 
62
72
  sig { returns(Symbol) }
@@ -10,7 +10,7 @@ module AtlasEngine
10
10
 
11
11
  sig { void }
12
12
  def update_result
13
- concern = InvalidZipConcernBuilder.for(address, [])
13
+ concern = InvalidZipConcernBuilder.for(address, [], message_format)
14
14
  result.concerns << concern if concern
15
15
 
16
16
  update_result_scope
@@ -24,7 +24,7 @@ module AtlasEngine
24
24
  type: T.must(Concern::TYPES[:error]),
25
25
  type_level: 3,
26
26
  suggestion_ids: [],
27
- message: @cache.country.field(key: :city).error(code: :blank).to_s,
27
+ message: @cache.country.field(key: :city).error(code: "blank_#{message_format.serialize}".to_sym).to_s,
28
28
  )
29
29
  end
30
30
  end
@@ -23,7 +23,11 @@ module AtlasEngine
23
23
  type: T.must(Concern::TYPES[:error]),
24
24
  type_level: 3,
25
25
  suggestion_ids: [],
26
- message: Worldwide.region(code: "US").field(key: :country).error(code: :blank),
26
+ message: Worldwide
27
+ .region(code: "US")
28
+ .field(key: :country)
29
+ .error(code: "blank_#{message_format.serialize}".to_sym)
30
+ .to_s,
27
31
  )
28
32
  end
29
33
  end
@@ -34,7 +34,7 @@ module AtlasEngine
34
34
  type_level: 1,
35
35
  suggestion_ids: [T.must(suggestion.id)],
36
36
  message: @cache.country.field(key: :zip).error(
37
- code: :invalid_for_country,
37
+ code: "invalid_for_country_#{message_format.serialize}".to_sym,
38
38
  options: { country: @cache.country.full_name },
39
39
  ),
40
40
  suggestion: suggestion,