atlas_engine 1.1.0 → 1.2.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -17
  3. data/app/graphql/atlas_engine/schema.graphql +10 -1
  4. data/app/graphql/atlas_engine/types/address_validation/concern_type.rb +8 -1
  5. data/app/graphql/atlas_engine/types/address_validation/suggestion_type.rb +4 -0
  6. data/app/graphql/atlas_engine/types/message_format_type.rb +11 -0
  7. data/app/graphql/atlas_engine/types/query_type.rb +3 -1
  8. data/app/lib/atlas_engine/validation_transcriber/address_parsings.rb +14 -9
  9. data/app/models/atlas_engine/address_validation/es/validators/full_address.rb +18 -5
  10. data/app/models/atlas_engine/address_validation/es/validators/full_address_street.rb +2 -2
  11. data/app/models/atlas_engine/address_validation/full_address_validator_base.rb +6 -2
  12. data/app/models/atlas_engine/address_validation/message_format.rb +13 -0
  13. data/app/models/atlas_engine/address_validation/predicate_pipeline_builder.rb +90 -0
  14. data/app/models/atlas_engine/address_validation/request.rb +1 -0
  15. data/app/models/atlas_engine/address_validation/suggestion.rb +26 -1
  16. data/app/models/atlas_engine/address_validation/validator.rb +31 -14
  17. data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result.rb +9 -5
  18. data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result_base.rb +4 -3
  19. data/app/models/atlas_engine/address_validation/validators/full_address/concern_builder.rb +19 -4
  20. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_concern_builder.rb +4 -3
  21. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_for_country_concern_builder.rb +6 -4
  22. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_for_province_concern_builder.rb +4 -2
  23. data/app/models/atlas_engine/address_validation/validators/full_address/no_candidate_result.rb +1 -1
  24. data/app/models/atlas_engine/address_validation/validators/full_address/suggestion_builder.rb +5 -2
  25. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_address_concern_builder.rb +16 -4
  26. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_province_concern_builder.rb +8 -3
  27. data/app/models/atlas_engine/address_validation/validators/full_address/unmatched_field_concern_builder.rb +13 -3
  28. data/app/models/atlas_engine/address_validation/validators/full_address/unsupported_script_result.rb +1 -1
  29. data/app/models/atlas_engine/address_validation/validators/predicates/city/present.rb +1 -1
  30. data/app/models/atlas_engine/address_validation/validators/predicates/country/exists.rb +5 -1
  31. data/app/models/atlas_engine/address_validation/validators/predicates/country/valid_for_zip.rb +1 -1
  32. data/app/models/atlas_engine/address_validation/validators/predicates/neighborhood/present.rb +35 -0
  33. data/app/models/atlas_engine/address_validation/validators/predicates/not_exceed_max_length.rb +18 -3
  34. data/app/models/atlas_engine/address_validation/validators/predicates/phone/valid.rb +3 -1
  35. data/app/models/atlas_engine/address_validation/validators/predicates/predicate.rb +6 -2
  36. data/app/models/atlas_engine/address_validation/validators/predicates/province/exists.rb +3 -1
  37. data/app/models/atlas_engine/address_validation/validators/predicates/province/valid_for_country.rb +3 -1
  38. data/app/models/atlas_engine/address_validation/validators/predicates/street/present.rb +3 -1
  39. data/app/models/atlas_engine/address_validation/validators/predicates/street_name/present.rb +35 -0
  40. data/app/models/atlas_engine/address_validation/validators/predicates/street_number/present.rb +35 -0
  41. data/app/models/atlas_engine/address_validation/validators/predicates/zip/present.rb +2 -2
  42. data/app/models/atlas_engine/address_validation/validators/predicates/zip/valid_for_country.rb +1 -1
  43. data/app/models/atlas_engine/address_validation/validators/predicates/zip/valid_for_province.rb +1 -1
  44. data/app/models/atlas_engine/services/validation.rb +4 -0
  45. data/db/data/validation_pipelines/es.yml +46 -0
  46. data/db/data/validation_pipelines/es_street.yml +46 -0
  47. data/db/data/validation_pipelines/local.yml +46 -0
  48. data/lib/atlas_engine/version.rb +1 -1
  49. metadata +39 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dde673ac6063402f30381011574f018aee19ac9d6e9b961c04252cf76cfd49e5
4
- data.tar.gz: b5c0adce91a39d744004b902e2b3a4a13544a50af756bb1ace003e391b0d3643
3
+ metadata.gz: 2ac0ad464ab86fe1db96b5ca3f5a69254d6ad52571d8c83350645c9e031ea58b
4
+ data.tar.gz: f683950cfa6d89c1cdf4935d8a49d76e416c57220ac29e5aa62adcdaf2a628d5
5
5
  SHA512:
6
- metadata.gz: dfe8b1e2a4ebe506f55b62a541b57ab22c0cca456ed4ef071007d43e106d0ed0b904389fd22a0424661ca76cd2f7bdc4ed5887866663eaee361f9afd5e7d3ac0
7
- data.tar.gz: a632f397142d60a6da29ae440ed686ff920cb15e62349c3abb0ef9a3ccaddd975936870d55aee45867851337f10d46982f6740bbcf3379f9d74aeeed15369577
6
+ metadata.gz: 79ae2f83d4b4ee2d65a7d6e566bee41ac3b0c5e44f879090b4659151ca9f7987e128badf6f31569530c142ea683b8df6dce10275423469924c44f1477f6480d6
7
+ data.tar.gz: e1a0e889113742443408d479dba7f1bb294230d4bcfd0af8f128e6c200a19d6c595c1bdf420798ebd2a9cb9caa8fa2cd69ef21ab4361ae066aa6a48861e9f91d
data/README.md CHANGED
@@ -25,7 +25,6 @@ query validation {
25
25
  zip: "K2P 2L8"
26
26
  }
27
27
  locale: "en"
28
- matchingStrategy: LOCAL
29
28
  ) {
30
29
  validationScope
31
30
  concerns {
@@ -70,21 +69,6 @@ presence or absence differs per country.
70
69
 
71
70
  **Locale:** The language in which to render any messages in the validation API response.
72
71
 
73
- **MatchingStrategy:** The strategy used to evaluate the validity of the address input. Out of the box, Atlas Engine
74
- supports three different matching strategies: `local`, `es`, and `es_street`.
75
- - `local` matching uses the [worldwide](https://github.com/Shopify/worldwide) gem to provide the most basic level of
76
- address validation. This may include simple errors (required fields not populated) or more advanced errors (province
77
- not belonging to the country, zip code not belonging to the province). This level of matching does not require
78
- [ingestion](#ingestion) of country data to work, but the level of support and suggestions it can provide in its
79
- responses is minimal.
80
- - `es` matching uses data indexed in elasticsearch via our [ingestion](#ingestion) process to validate the city,
81
- province, country, and zip code fields of the input address, in addition to all of the basic functionality provided
82
- in the `local` strategy. A more detailed explanation for how this strategy works can be found [here](#elasticsearch-matching-strategy).
83
- - `es_street` is our most advanced matching strategy and requires the highest quality data indexed in elasticsearch
84
- via our [ingestion](#ingestion) process. This matching strategy provides everything that `es` and `local` does along
85
- with validation of the address1 and address2 components of the address input. A more detailed explanation of how
86
- this strategy works can be found [here](#elasticsearch-matching-strategy).
87
-
88
72
  **Validation Scope:** This response object is populated with the field names from the input that have been successfully
89
73
  validated.
90
74
 
@@ -109,7 +93,6 @@ query validation {
109
93
  zip: "90210"
110
94
  }
111
95
  locale: "en"
112
- matchingStrategy: LOCAL
113
96
  ) {
114
97
  validationScope
115
98
  concerns {
@@ -410,6 +393,20 @@ and the US data is in mysql the rest of the process for creating the elasticsear
410
393
 
411
394
  ## Elasticsearch Matching Strategy
412
395
 
396
+ An optional GraphQL parameter, and the strategy used to evaluate the validity of the address input. Out of the box, Atlas Engine
397
+ supports three different matching strategies: `local`, `es`, and `es_street`.
398
+ - `local` matching uses the [worldwide](https://github.com/Shopify/worldwide) gem to provide the most basic level of
399
+ address validation. This may include simple errors (required fields not populated) or more advanced errors (province
400
+ not belonging to the country, zip code not belonging to the province). This level of matching does not require
401
+ [ingestion](#ingestion) of country data to work, but the level of support and suggestions it can provide in its
402
+ responses is minimal.
403
+ - `es` matching uses data indexed in elasticsearch via our [ingestion](#ingestion) process to validate the city,
404
+ province, country, and zip code fields of the input address, in addition to all of the basic functionality provided
405
+ in the `local` strategy.
406
+ - `es_street` is our most advanced matching strategy and requires the highest quality data indexed in elasticsearch
407
+ via our [ingestion](#ingestion) process. This matching strategy provides everything that `es` and `local` does along
408
+ with validation of the address1 and address2 components of the address input.
409
+
413
410
  Once we have successfully created and activated an elasticsearch index using open address data, we may now use
414
411
  the more advanced elasticsearch matching strategies `es` and `es_street`.
415
412
 
@@ -47,8 +47,13 @@ enum MatchingStrategy {
47
47
  LOCAL
48
48
  }
49
49
 
50
+ enum MessageFormat {
51
+ INFORMATIVE
52
+ INSTRUCTIONAL
53
+ }
54
+
50
55
  type Query {
51
- validation(address: AddressInput!, locale: String!, matchingStrategy: MatchingStrategy): Validation!
56
+ validation(address: AddressInput!, locale: String!, matchingStrategy: MatchingStrategy, messageFormat: MessageFormat): Validation!
52
57
  }
53
58
 
54
59
  """
@@ -60,8 +65,12 @@ type Suggestion {
60
65
  city: String
61
66
  countryCode: ValidationSupportedCountry
62
67
  id: String!
68
+ line2: String
69
+ neighborhood: String
63
70
  province: String
64
71
  provinceCode: String
72
+ streetName: String
73
+ streetNumber: String
65
74
  zip: String
66
75
  }
67
76
 
@@ -1,10 +1,12 @@
1
- # typed: false
1
+ # typed: true
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module AtlasEngine
5
5
  module Types
6
6
  module AddressValidation
7
7
  class ConcernType < BaseObject
8
+ extend T::Sig
9
+
8
10
  description "A piece of relevant information regarding the address validation result." \
9
11
  "They can be categorized as either a Warning or Error."
10
12
 
@@ -14,6 +16,11 @@ module AtlasEngine
14
16
  field :type, Enums::ConcernEnum, null: false
15
17
  field :type_level, Int, null: false
16
18
  field :suggestion_ids, [String], null: false
19
+
20
+ sig { returns(T::Array[String]) }
21
+ def field_names
22
+ object.field_names.map { |field_name| field_name.to_s.camelize(:lower) }
23
+ end
17
24
  end
18
25
  end
19
26
  end
@@ -9,7 +9,11 @@ module AtlasEngine
9
9
 
10
10
  field :id, String, null: false
11
11
  field :address1, String, null: true
12
+ field :street_name, String, null: true
13
+ field :street_number, String, null: true
12
14
  field :address2, String, null: true
15
+ field :line2, String, null: true
16
+ field :neighborhood, String, null: true
13
17
  field :city, String, null: true
14
18
  field :zip, String, null: true
15
19
  field :province_code, String, null: true
@@ -0,0 +1,11 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module Types
6
+ class MessageFormatType < BaseEnum
7
+ value :INSTRUCTIONAL, value: "instructional"
8
+ value :INFORMATIVE, value: "informative"
9
+ end
10
+ end
11
+ end
@@ -10,9 +10,10 @@ module AtlasEngine
10
10
  argument :address, AddressValidation::AddressInput, required: true
11
11
  argument :locale, String, required: true
12
12
  argument :matching_strategy, MatchingStrategyType, required: false
13
+ argument :message_format, MessageFormatType, required: false
13
14
  end
14
15
 
15
- def validation(address:, locale: "en", matching_strategy: nil)
16
+ def validation(address:, locale: "en", matching_strategy: nil, message_format: nil)
16
17
  raise build_graphql_error(AtlasEngine::AddressValidation::Errors::MISSING_PARAMETER) if address.blank?
17
18
 
18
19
  locale = LocaleFormatHelper.format_locale(locale)
@@ -26,6 +27,7 @@ module AtlasEngine
26
27
  address: address,
27
28
  locale: locale,
28
29
  matching_strategy: matching_strategy,
30
+ message_format: message_format,
29
31
  ),
30
32
  )
31
33
  end
@@ -9,22 +9,27 @@ module AtlasEngine
9
9
 
10
10
  ParsedComponents = T.type_alias { T::Hash[Symbol, String] }
11
11
 
12
- sig { returns(T::Array[ParsedComponents]) }
13
- attr_reader :parsings
14
-
15
12
  sig { params(address_input: AddressValidation::AbstractAddress, locale: T.nilable(String)).void }
16
13
  def initialize(address_input:, locale: nil)
17
- @parsings = T.let(
14
+ @address_input = address_input
15
+ @locale = locale
16
+ end
17
+
18
+ sig { returns(T::Array[ParsedComponents]) }
19
+ def parsings
20
+ @parsings ||= T.let(
18
21
  begin
19
- if address_input.country_code.blank?
22
+ if @address_input.country_code.blank?
20
23
  []
21
- else
22
- parsing_result = AddressParserFactory.create(address: address_input, locale: locale).parse
23
- log_unparsable_address(address_input) if parsing_result.empty?
24
+ elsif @address_input.street_name.nil? && @address_input.street_number.nil?
25
+ parsing_result = AddressParserFactory.create(address: @address_input, locale: @locale).parse
26
+ log_unparsable_address(@address_input) if parsing_result.empty?
24
27
  parsing_result
28
+ else
29
+ [{ street: @address_input.street_name, building_num: @address_input.street_number }]
25
30
  end
26
31
  end,
27
- T::Array[ParsedComponents],
32
+ T.nilable(T::Array[ParsedComponents]),
28
33
  )
29
34
  end
30
35
 
@@ -8,12 +8,13 @@ module AtlasEngine
8
8
  class FullAddress < FullAddressValidatorBase
9
9
  include LogHelper
10
10
 
11
- attr_reader :address, :result
11
+ attr_reader :address, :result, :message_format
12
12
 
13
- sig { params(address: TAddress, result: Result).void }
14
- def initialize(address:, result: Result.new)
13
+ sig { params(address: TAddress, result: Result, message_format: MessageFormat).void }
14
+ def initialize(address:, result: Result.new, message_format: MessageFormat::Instructional)
15
15
  super
16
16
  @matching_strategy = MatchingStrategies::Es
17
+ @message_format = message_format
17
18
  end
18
19
 
19
20
  sig { override.returns(Result) }
@@ -29,16 +30,23 @@ module AtlasEngine
29
30
  sig { returns(AddressValidation::Validators::FullAddress::CandidateResultBase) }
30
31
  def build_candidate_result
31
32
  unless supported_address?(address)
32
- return AddressValidation::Validators::FullAddress::UnsupportedScriptResult.new(address:, result:)
33
+ return AddressValidation::Validators::FullAddress::UnsupportedScriptResult.new(
34
+ address:,
35
+ result:,
36
+ message_format:,
37
+ )
33
38
  end
34
39
 
35
40
  if best_candidate.nil?
36
- AddressValidation::Validators::FullAddress::NoCandidateResult.new(address:, result:)
41
+ AddressValidation::Validators::FullAddress::NoCandidateResult.new(
42
+ address:, result:, message_format:,
43
+ )
37
44
  else
38
45
  AddressValidation::Validators::FullAddress::CandidateResult.new(
39
46
  address_comparison: T.must(best_candidate),
40
47
  matching_strategy: @matching_strategy,
41
48
  result: result,
49
+ message_format:,
42
50
  )
43
51
  end
44
52
  end
@@ -100,6 +108,10 @@ module AtlasEngine
100
108
  :zip,
101
109
  :address1,
102
110
  :address2,
111
+ :street_name,
112
+ :street_number,
113
+ :neighborhood,
114
+ :line2,
103
115
  ])
104
116
  end
105
117
 
@@ -108,6 +120,7 @@ module AtlasEngine
108
120
  result.concerns.flat_map(&:code).intersect?([
109
121
  :address1_contains_too_many_words,
110
122
  :address2_contains_too_many_words,
123
+ :street_name_contains_too_many_words,
111
124
  ])
112
125
  end
113
126
 
@@ -6,8 +6,8 @@ module AtlasEngine
6
6
  module Es
7
7
  module Validators
8
8
  class FullAddressStreet < FullAddress
9
- sig { params(address: TAddress, result: Result).void }
10
- def initialize(address:, result: Result.new)
9
+ sig { params(address: TAddress, message_format: MessageFormat, result: Result).void }
10
+ def initialize(address:, message_format:, result: Result.new)
11
11
  super
12
12
  @matching_strategy = MatchingStrategies::EsStreet
13
13
  end
@@ -14,10 +14,14 @@ module AtlasEngine
14
14
  sig { returns(AbstractAddress) }
15
15
  attr_reader :address
16
16
 
17
- sig { params(address: AbstractAddress, result: Result).void }
18
- def initialize(address:, result:)
17
+ sig { returns(MessageFormat) }
18
+ attr_reader :message_format
19
+
20
+ sig { params(address: AbstractAddress, result: Result, message_format: MessageFormat).void }
21
+ def initialize(address:, result:, message_format: MessageFormat::Instructional)
19
22
  @address = address
20
23
  @result = result
24
+ @message_format = message_format
21
25
  end
22
26
 
23
27
  sig { abstract.returns(Result) }
@@ -0,0 +1,13 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class MessageFormat < T::Enum
7
+ enums do
8
+ Instructional = new("instructional")
9
+ Informative = new("informative")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -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