belpost 0.4.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d81d235b373255e2bff25ee56958cd6658a44e9546560ae96486986d278c1f0b
4
- data.tar.gz: a155cca07102f767c12b05bf2c574fdb827f46cae00357e0b213c4daf0d35c84
3
+ metadata.gz: 5555f3f61fcb03030a1e5fd226abac810104277326db2ee42b505656ff0f3ce8
4
+ data.tar.gz: 585c4832344b4cfa9a654e4605926bacd605bf854e77e21391910de03d3d22db
5
5
  SHA512:
6
- metadata.gz: cf7a95cd9ae059a9f0d4e10c3ac7e24cf7f94447817d9d104a58a2d2eb9382f4c13ea1fe5bb512589a9e92141ea92100632864a0637ac47150eed848af1d11d7
7
- data.tar.gz: 4fa75a2f2af404733bcf1eff337ac0c102629d8c8ae67085e44858174e9f17975ffb70cced4e5444834801419391e484c340e5d9c87813ec1ce105f9616e2687
6
+ metadata.gz: eaf547611c62aa03df55771a57d4405ea1352b04cf9fe3fddd04f1120b3df24a100a38e4f449e7dcadffa1a7b1bd58101212425d263d7e168b448712af597d5c
7
+ data.tar.gz: 4eb26318bd0e28467e50a31caa184880fab8276e331efcd7240723124ebf3795615d47eca35516f7f0d5048dbf3bb44663bb25182815d807fd4aef9d59313899
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.0] - 2025-04-01
4
+ ### Added
5
+ - Added postal code search functionality via `search_postcode` method
6
+ - Support for searching postal codes by city, street, and building number
7
+ - Added comprehensive tests for the new postal code search endpoint
8
+
9
+ ## [0.6.0] - 2025-04-01
10
+ ### Added
11
+ - Added search addresses search functionality via `find_address_by_string` method
12
+
13
+ ## [0.5.1] - 2025-04-01
14
+ ### Fixed
15
+ - Improved error handling for invalid timeout environment variable
16
+ - Added fallback for BELPOST_API_URL environment variable to make tests more robust
17
+ - Fixed Configuration class by implementing `validate!` and `to_h` methods
18
+
19
+ ## [0.5.0] - 2025-04-01
20
+ ### Added
21
+ - Added address search functionality via `find_address_by_string` method
22
+ - Support for query parameters in GET requests
23
+
3
24
  ## [0.4.0] - 2025-04-01
4
25
  ### Added
5
26
  - Added configuration via environment variables
data/README.md CHANGED
@@ -295,6 +295,19 @@ hs_codes = client.fetch_hs_codes
295
295
  puts hs_codes
296
296
  ```
297
297
 
298
+ ### Searching for postal codes
299
+
300
+ ```ruby
301
+ client = Belpost::Client.new
302
+ postcodes = client.search_postcode(
303
+ city: "Витебск",
304
+ street: "Ильинского",
305
+ building: "51/1", # optional
306
+ limit: 50 # optional, default: 50, range: 1-200
307
+ )
308
+ puts postcodes
309
+ ```
310
+
298
311
  ## Error handling
299
312
 
300
313
  The client may throw the following exceptions:
@@ -23,10 +23,12 @@ module Belpost
23
23
  # Performs a GET request to the specified path.
24
24
  #
25
25
  # @param path [String] The API endpoint path.
26
+ # @param params [Hash] The query parameters (default: {}).
26
27
  # @return [Models::ApiResponse] The parsed JSON response from the API.
27
- def get(path)
28
+ def get(path, params = {})
28
29
  Retry.with_retry do
29
30
  uri = URI("#{@base_url}#{path}")
31
+ uri.query = URI.encode_www_form(params) unless params.empty?
30
32
  request = Net::HTTP::Get.new(uri)
31
33
  add_headers(request)
32
34
 
@@ -3,6 +3,7 @@
3
3
  require_relative "api_service"
4
4
  require_relative "models/parcel"
5
5
  require_relative "models/api_response"
6
+ require_relative "validations/address_schema"
6
7
 
7
8
  module Belpost
8
9
  # Main client class for interacting with the BelPost API.
@@ -67,5 +68,75 @@ module Belpost
67
68
  response = @api_service.get("/api/v1/business/postal-deliveries/countries")
68
69
  response.to_h
69
70
  end
71
+
72
+ # Allows you to find an address by a string.
73
+ #
74
+ # Accepts a string with an address in any form and returns found addresses (up to 50 records).
75
+ # Building numbers should be specified without spaces: "building number""letter""building".
76
+ # The letter should be uppercase, and "building" (or "корп", "кор", "к") should be replaced with "/".
77
+ # Example: "город Минск улица Автодоровская 3Е корпус 4" should be transformed to "город Минск улица Автодоровская 3Е/4".
78
+ #
79
+ # @param address [String] The address string to search for.
80
+ # @return [Array<Hash>] An array of found addresses with postcode, region, city, street and other information.
81
+ # @raise [Belpost::ApiError] If the API returns an error response.
82
+ # @raise [Belpost::InvalidRequestError] If the address parameter is missing or has an incorrect format.
83
+ def find_address_by_string(address)
84
+ raise ValidationError, "Address must be filled" if address.nil?
85
+ raise ValidationError, "Address must be a string" unless address.is_a?(String)
86
+ raise ValidationError, "Address must be filled" if address.empty?
87
+
88
+ formatted_address = format_address(address)
89
+ response = @api_service.get("/api/v1/business/geo-directory/search-address", { search: formatted_address })
90
+ response.to_h
91
+ end
92
+
93
+ # Searches for postal codes by city, street, and building number.
94
+ #
95
+ # @param city [String] The city name (required)
96
+ # @param street [String] The street name (required)
97
+ # @param building [String] The building number (optional)
98
+ # @param limit [Integer] Maximum number of results (optional, default: 50, range: 1-200)
99
+ # @return [Array<Hash>] An array of found addresses with postcode, region, city, street and other information
100
+ # @raise [Belpost::ValidationError] If required parameters are missing or invalid
101
+ # @raise [Belpost::ApiError] If the API returns an error response
102
+ def search_postcode(city:, street:, building: nil, limit: 50)
103
+ raise ValidationError, "City must be filled" if city.nil?
104
+ raise ValidationError, "City must be a string" unless city.is_a?(String)
105
+ raise ValidationError, "City must be filled" if city.empty?
106
+ raise ValidationError, "Street must be filled" if street.nil?
107
+ raise ValidationError, "Street must be a string" unless street.is_a?(String)
108
+ raise ValidationError, "Street must be filled" if street.empty?
109
+ raise ValidationError, "Building must be a string" if building && !building.is_a?(String)
110
+ raise ValidationError, "Limit must be between 1 and 200" if limit < 1 || limit > 200
111
+
112
+ params = { city: city, street: street }
113
+ params[:building] = format_building_number(building) if building
114
+ params[:limit] = limit
115
+
116
+ response = @api_service.get("/api/v1/business/geo-directory/postcode", params)
117
+ response.to_h
118
+ end
119
+
120
+ private
121
+
122
+ def format_address(address)
123
+ address.gsub(/\s+/, " ")
124
+ .gsub(/\s*корпус\s*(\d+)\s*/i, '/\1')
125
+ .gsub(/\s*корп\s*(\d+)\s*/i, '/\1')
126
+ .gsub(/\s*кор\s*(\d+)\s*/i, '/\1')
127
+ .gsub(/\s*к\s*(\d+)\s*/i, '/\1')
128
+ .strip
129
+ end
130
+
131
+ def format_building_number(building)
132
+ return building unless building
133
+
134
+ building.gsub(/\s+/, " ")
135
+ .gsub(/\s*корпус\s*(\d+)\s*/i, '/\1')
136
+ .gsub(/\s*корп\s*(\d+)\s*/i, '/\1')
137
+ .gsub(/\s*кор\s*(\d+)\s*/i, '/\1')
138
+ .gsub(/\s*к\s*(\d+)\s*/i, '/\1')
139
+ .strip
140
+ end
70
141
  end
71
142
  end
@@ -7,9 +7,32 @@ module Belpost
7
7
  attr_accessor :base_url, :jwt_token, :timeout
8
8
 
9
9
  def initialize
10
- @base_url = ENV.fetch("BELPOST_API_URL")
10
+ @base_url = ENV.fetch("BELPOST_API_URL", "https://api.belpost.by")
11
11
  @jwt_token = ENV.fetch("BELPOST_JWT_TOKEN", nil)
12
- @timeout = ENV.fetch("BELPOST_TIMEOUT", 10).to_i
12
+
13
+ # Convert timeout to integer with a fallback to default
14
+ begin
15
+ @timeout = Integer(ENV.fetch("BELPOST_TIMEOUT", 10))
16
+ rescue ArgumentError
17
+ @timeout = 10
18
+ end
19
+ end
20
+
21
+ # Validates that all required configuration is present
22
+ # @raise [Belpost::ConfigurationError] If required configuration is missing
23
+ def validate!
24
+ raise ConfigurationError, "Base URL is required" if base_url.nil?
25
+ raise ConfigurationError, "JWT token is required" if jwt_token.nil?
26
+ end
27
+
28
+ # Returns a hash representation of the configuration
29
+ # @return [Hash] The configuration as a hash
30
+ def to_h
31
+ {
32
+ base_url: base_url,
33
+ jwt_token: jwt_token,
34
+ timeout: timeout
35
+ }
13
36
  end
14
37
  end
15
38
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-validation"
4
+
5
+ module Belpost
6
+ module Validation
7
+ # Schema for validating and formatting address strings
8
+ class AddressSchema < Dry::Validation::Contract
9
+ params do
10
+ required(:address).filled(:string)
11
+ end
12
+
13
+ rule(:address) do
14
+ # Remove extra spaces
15
+ address = value.gsub(/\s+/, " ").strip
16
+
17
+ # Replace building indicators with "/"
18
+ address = address.gsub(/\s+(корпус|корп|кор|к)\s+/, "/")
19
+
20
+ # Ensure building number format (no spaces between number, letter, and building)
21
+ address = address.gsub(/(\d+)\s*([А-Я])\s*(\d+)/, '\1\2/\3')
22
+
23
+ # Ensure letter is uppercase
24
+ address = address.gsub(/(\d+)([а-я])(\/\d+)/) { |m| "#{$1}#{$2.upcase}#{$3}" }
25
+
26
+ key.failure(address)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Belpost
4
- VERSION = "0.4.0"
4
+ VERSION = "0.7.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belpost
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - KuberLite
@@ -64,6 +64,7 @@ files:
64
64
  - lib/belpost/models/parcel.rb
65
65
  - lib/belpost/models/parcel_builder.rb
66
66
  - lib/belpost/retry.rb
67
+ - lib/belpost/validations/address_schema.rb
67
68
  - lib/belpost/validations/parcel_schema.rb
68
69
  - lib/belpost/version.rb
69
70
  homepage: https://github.com/KuberLite/belpost