belpost 0.1.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: c53d02403855e383fbb7d309ddeeeec66b2065303321cdde544d0e4fa6aba93e
4
- data.tar.gz: aef5e4c2911ae80fca64dff3fb347cbf270ca66ea82da3905d3446a65efd543e
3
+ metadata.gz: 5555f3f61fcb03030a1e5fd226abac810104277326db2ee42b505656ff0f3ce8
4
+ data.tar.gz: 585c4832344b4cfa9a654e4605926bacd605bf854e77e21391910de03d3d22db
5
5
  SHA512:
6
- metadata.gz: cfef1ca328d78d3ff55335a2271ba505e02ef2a95af81924a43acc736fc1a09828197a5075bc501d219ed60847dcf606eea082a68d7617769a5cbc6a94395ff4
7
- data.tar.gz: ffe48fcfa53026d756eccc552db0cdac37c17aed4b14489852d51a51d2a6ae97e9f931bbd7babcd2842232003f807152c550e315f7c9bee34446955994cb05f4
6
+ metadata.gz: eaf547611c62aa03df55771a57d4405ea1352b04cf9fe3fddd04f1120b3df24a100a38e4f449e7dcadffa1a7b1bd58101212425d263d7e168b448712af597d5c
7
+ data.tar.gz: 4eb26318bd0e28467e50a31caa184880fab8276e331efcd7240723124ebf3795615d47eca35516f7f0d5048dbf3bb44663bb25182815d807fd4aef9d59313899
data/.env.test.example ADDED
@@ -0,0 +1,11 @@
1
+ # Example environment variables for testing
2
+ # Copy this file to .env.test and adjust values as needed
3
+
4
+ # API URL for testing (required)
5
+ BELPOST_API_URL=https://test-api.belpost.by
6
+
7
+ # Test JWT token - not a real token
8
+ BELPOST_JWT_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRlc3QgVXNlciIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
9
+
10
+ # Timeout for API requests in seconds
11
+ BELPOST_TIMEOUT=5
data/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # Changelog
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
+
24
+ ## [0.4.0] - 2025-04-01
25
+ ### Added
26
+ - Added configuration via environment variables
27
+ - Added test coverage and CI setup
28
+ - Added basic documentation in README
29
+ - Added .env.test.example for testing setup
30
+ - Added spec helper with test configuration
31
+
32
+
33
+ ## [0.1.0] - 2025-03-31
34
+ ### Added
35
+ - Initial gem version
36
+ - Core classes for interacting with the Belpost API
37
+ - Support for creating parcels
38
+ - Data validation before sending to API
39
+ - Error handling and request retries
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,63 @@
1
+ # Contributing Guide
2
+
3
+ ## Development Environment Setup
4
+
5
+ 1. Fork the repository and clone it:
6
+ ```
7
+ git clone https://github.com/YOUR_USERNAME/belpost.git
8
+ cd belpost
9
+ ```
10
+
11
+ 2. Install dependencies:
12
+ ```
13
+ bundle install
14
+ ```
15
+
16
+ 3. Run tests to make sure everything works:
17
+ ```
18
+ bundle exec rspec
19
+ ```
20
+
21
+ ## Development Process
22
+
23
+ 1. Create a new branch for your feature or fix:
24
+ ```
25
+ git checkout -b feature/your-feature-name
26
+ ```
27
+
28
+ 2. Make changes and add tests for new functionality.
29
+
30
+ 3. Make sure all tests pass:
31
+ ```
32
+ bundle exec rspec
33
+ ```
34
+
35
+ 4. Update documentation if you've added new features or changed existing ones.
36
+
37
+ 5. Commit your changes:
38
+ ```
39
+ git commit -am "Added new feature: XYZ"
40
+ ```
41
+
42
+ 6. Push your branch to GitHub:
43
+ ```
44
+ git push origin feature/your-feature-name
45
+ ```
46
+
47
+ 7. Create a Pull Request from your branch to the main repository.
48
+
49
+ ## Code Conventions
50
+
51
+ - Follow the code style used in the project
52
+ - Write YARD format documentation for new classes and methods
53
+ - Always add tests for new functionality
54
+ - Update CHANGELOG.md with descriptions of your changes in the [Unreleased] section
55
+
56
+ ## Review Process
57
+
58
+ After creating a Pull Request:
59
+ 1. Wait for all CI checks to pass
60
+ 2. Respond to comments and make necessary corrections
61
+ 3. After approval, your Pull Request will be merged into the main branch
62
+
63
+ Thank you for your contribution!
data/README.md CHANGED
@@ -1,54 +1,57 @@
1
1
  # Belpost
2
2
 
3
- Клиент для работы с Belpochta API (Белпочта).
3
+ Client for working with Belpochta API (Belpost).
4
4
 
5
- ## Установка
5
+ ![Tests](https://github.com/KuberLite/belpost/actions/workflows/test.yml/badge.svg)
6
+ ![Gem Version](https://badge.fury.io/rb/belpost.svg)
6
7
 
7
- Добавьте эту строку в Gemfile вашего приложения:
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
8
11
 
9
12
  ```ruby
10
13
  gem 'belpost'
11
14
  ```
12
15
 
13
- И выполните:
16
+ And then execute:
14
17
 
15
18
  ```bash
16
19
  $ bundle install
17
20
  ```
18
21
 
19
- Или установите самостоятельно:
22
+ Or install it yourself:
20
23
 
21
24
  ```bash
22
25
  $ gem install belpost
23
26
  ```
24
27
 
25
- ## Настройка
28
+ ## Configuration
26
29
 
27
- Настройте клиент для работы с API Белпочты:
30
+ Configure the client to work with the Belpost API:
28
31
 
29
32
  ```ruby
30
33
  require 'belpost'
31
34
 
32
35
  Belpost.configure do |config|
33
- config.jwt_token = 'ваш_jwt_токен_от_Белпочты'
36
+ config.jwt_token = 'your_jwt_token_from_belpost'
34
37
  config.base_url = 'https://api.belpost.by'
35
- config.timeout = 30 # Таймаут в секундах (по умолчанию 10)
38
+ config.timeout = 30 # Timeout in seconds (default 10)
36
39
  end
37
40
  ```
38
41
 
39
- Вы также можете использовать переменные окружения:
42
+ You can also use environment variables:
40
43
 
41
44
  ```
42
- BELPOST_JWT_TOKEN=ваш_jwt_токен_от_Белпочты
45
+ BELPOST_JWT_TOKEN=your_jwt_token_from_belpost
43
46
  BELPOST_BASE_URL=https://api.belpost.by
44
47
  BELPOST_TIMEOUT=30
45
48
  ```
46
49
 
47
- ## Использование
50
+ ## Usage
48
51
 
49
- ### Создание посылки
52
+ ### Creating a parcel
50
53
 
51
- #### Базовый пример
54
+ #### Basic example
52
55
 
53
56
  ```ruby
54
57
  client = Belpost::Client.new
@@ -82,23 +85,23 @@ parcel_data = {
82
85
  sender: {
83
86
  type: "legal_person",
84
87
  info: {
85
- organization_name: "ООО \"Компания\"",
88
+ organization_name: "LLC \"Company\"",
86
89
  taxpayer_number: "123456789",
87
90
  IBAN: "BY26BAPB30123418400100000000",
88
91
  BIC: "BAPBBY2X",
89
- bank: "ОАО 'БЕЛАГРОПРОМБАНК'"
92
+ bank: "JSC 'BELAGROPROMBANK'"
90
93
  },
91
94
  location: {
92
95
  code: "225212",
93
- region: "Брестская",
94
- district: "Березовский",
96
+ region: "Brest",
97
+ district: "Bereza",
95
98
  locality: {
96
- type: "город",
97
- name: "Береза"
99
+ type: "city",
100
+ name: "Bereza"
98
101
  },
99
102
  road: {
100
- type: "улица",
101
- name: "Ленина"
103
+ type: "street",
104
+ name: "Lenin"
102
105
  },
103
106
  building: "1",
104
107
  housing: "",
@@ -110,21 +113,21 @@ parcel_data = {
110
113
  recipient: {
111
114
  type: "natural_person",
112
115
  info: {
113
- first_name: "Иван",
114
- second_name: "Иванович",
115
- last_name: "Иванов"
116
+ first_name: "Ivan",
117
+ second_name: "Ivanovich",
118
+ last_name: "Ivanov"
116
119
  },
117
120
  location: {
118
121
  code: "231365",
119
- region: "Гродненская",
120
- district: "Ивьевский",
122
+ region: "Grodno",
123
+ district: "Ivye",
121
124
  locality: {
122
- type: "деревня",
123
- name: "Дуды"
125
+ type: "village",
126
+ name: "Dudy"
124
127
  },
125
128
  road: {
126
- type: "улица",
127
- name: "Центральная"
129
+ type: "street",
130
+ name: "Central"
128
131
  },
129
132
  building: "1",
130
133
  housing: "",
@@ -136,40 +139,40 @@ parcel_data = {
136
139
  }
137
140
 
138
141
  response = client.create_parcel(parcel_data)
139
- puts "Трекинг код: #{response["data"]["parcel"]["s10code"]}"
142
+ puts "Tracking code: #{response["data"]["parcel"]["s10code"]}"
140
143
  ```
141
144
 
142
- #### Использование ParcelBuilder
145
+ #### Using ParcelBuilder
143
146
 
144
147
  ```ruby
145
148
  client = Belpost::Client.new
146
149
 
147
- # Создание внутренней посылки
150
+ # Creating a domestic parcel
148
151
  parcel_data = Belpost::Models::ParcelBuilder.new
149
152
  .with_type("package")
150
153
  .with_attachment_type("products")
151
- .with_weight(1500) # вес в граммах
152
- .with_dimensions(300, 200, 100) # длина, ширина, высота в мм
154
+ .with_weight(1500) # weight in grams
155
+ .with_dimensions(300, 200, 100) # length, width, height in mm
153
156
  .to_country("BY")
154
157
  .with_declared_value(100)
155
158
  .with_cash_on_delivery(50)
156
159
  .add_service(:simple_notification)
157
160
  .add_service(:email_notification)
158
- .from_legal_person("ООО \"Компания\"")
161
+ .from_legal_person("LLC \"Company\"")
159
162
  .with_sender_details(
160
163
  taxpayer_number: "123456789",
161
- bank: "ОАО 'БЕЛАГРОПРОМБАНК'",
164
+ bank: "JSC 'BELAGROPROMBANK'",
162
165
  iban: "BY26BAPB30123418400100000000",
163
166
  bic: "BAPBBY2X"
164
167
  )
165
168
  .with_sender_location(
166
169
  postal_code: "225212",
167
- region: "Брестская",
168
- district: "Березовский",
169
- locality_type: "город",
170
- locality_name: "Береза",
171
- road_type: "улица",
172
- road_name: "Ленина",
170
+ region: "Brest",
171
+ district: "Bereza",
172
+ locality_type: "city",
173
+ locality_name: "Bereza",
174
+ road_type: "street",
175
+ road_name: "Lenin",
173
176
  building: "1"
174
177
  )
175
178
  .with_sender_contact(
@@ -177,18 +180,18 @@ parcel_data = Belpost::Models::ParcelBuilder.new
177
180
  phone: "375291234567"
178
181
  )
179
182
  .to_natural_person(
180
- first_name: "Иван",
181
- last_name: "Иванов",
182
- second_name: "Иванович"
183
+ first_name: "Ivan",
184
+ last_name: "Ivanov",
185
+ second_name: "Ivanovich"
183
186
  )
184
187
  .with_recipient_location(
185
188
  postal_code: "231365",
186
- region: "Гродненская",
187
- district: "Ивьевский",
188
- locality_type: "деревня",
189
- locality_name: "Дуды",
190
- road_type: "улица",
191
- road_name: "Центральная",
189
+ region: "Grodno",
190
+ district: "Ivye",
191
+ locality_type: "village",
192
+ locality_name: "Dudy",
193
+ road_type: "street",
194
+ road_name: "Central",
192
195
  building: "1"
193
196
  )
194
197
  .with_recipient_contact(
@@ -197,24 +200,24 @@ parcel_data = Belpost::Models::ParcelBuilder.new
197
200
  .build
198
201
 
199
202
  response = client.create_parcel(parcel_data)
200
- puts "Трекинг код: #{response["data"]["parcel"]["s10code"]}"
203
+ puts "Tracking code: #{response["data"]["parcel"]["s10code"]}"
201
204
  ```
202
205
 
203
- #### Создание международной посылки с таможенной декларацией
206
+ #### Creating an international parcel with a customs declaration
204
207
 
205
208
  ```ruby
206
209
  client = Belpost::Client.new
207
210
 
208
- # Создание таможенной декларации
211
+ # Creating a customs declaration
209
212
  customs_declaration = Belpost::Models::CustomsDeclaration.new
210
213
  customs_declaration.set_category("gift")
211
214
  customs_declaration.set_price("USD", 50)
212
215
  customs_declaration.add_item(
213
216
  {
214
- name: "Книга",
215
- local: "Книга",
217
+ name: "Book",
218
+ local: "Book",
216
219
  unit: {
217
- local: "ШТ",
220
+ local: "PCS",
218
221
  en: "PCS"
219
222
  },
220
223
  count: 1,
@@ -227,22 +230,22 @@ customs_declaration.add_item(
227
230
  }
228
231
  )
229
232
 
230
- # Создание международной посылки
233
+ # Creating an international parcel
231
234
  parcel_data = Belpost::Models::ParcelBuilder.new
232
235
  .with_type("package")
233
236
  .with_attachment_type("products")
234
237
  .with_weight(500)
235
- .to_country("DE") # Германия
238
+ .to_country("DE") # Germany
236
239
  .with_declared_value(50, "USD")
237
- .from_legal_person("ООО \"Компания\"")
240
+ .from_legal_person("LLC \"Company\"")
238
241
  .with_sender_location(
239
242
  postal_code: "225212",
240
- region: "Брестская",
241
- district: "Березовский",
242
- locality_type: "город",
243
- locality_name: "Береза",
244
- road_type: "улица",
245
- road_name: "Ленина",
243
+ region: "Brest",
244
+ district: "Bereza",
245
+ locality_type: "city",
246
+ locality_name: "Bereza",
247
+ road_type: "street",
248
+ road_name: "Lenin",
246
249
  building: "1"
247
250
  )
248
251
  .with_sender_contact(
@@ -265,10 +268,10 @@ parcel_data = Belpost::Models::ParcelBuilder.new
265
268
  .build
266
269
 
267
270
  response = client.create_parcel(parcel_data)
268
- puts "Трекинг код: #{response["data"]["parcel"]["s10code"]}"
271
+ puts "Tracking code: #{response["data"]["parcel"]["s10code"]}"
269
272
  ```
270
273
 
271
- ### Получение списка доступных стран
274
+ ### Getting a list of available countries
272
275
 
273
276
  ```ruby
274
277
  client = Belpost::Client.new
@@ -276,7 +279,7 @@ countries = client.fetch_available_countries
276
279
  puts countries
277
280
  ```
278
281
 
279
- ### Получение данных для валидации почтового отправления
282
+ ### Obtaining data for postal item validation
280
283
 
281
284
  ```ruby
282
285
  client = Belpost::Client.new
@@ -284,7 +287,7 @@ validation_data = client.validate_postal_delivery("BY")
284
287
  puts validation_data
285
288
  ```
286
289
 
287
- ### Получение кодов HS для таможенного декларирования
290
+ ### Obtaining HS codes for customs declaration
288
291
 
289
292
  ```ruby
290
293
  client = Belpost::Client.new
@@ -292,43 +295,84 @@ hs_codes = client.fetch_hs_codes
292
295
  puts hs_codes
293
296
  ```
294
297
 
295
- ## Обработка ошибок
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
+
311
+ ## Error handling
296
312
 
297
- Клиент может выбрасывать следующие исключения:
313
+ The client may throw the following exceptions:
298
314
 
299
- - `Belpost::ConfigurationError` - ошибка конфигурации
300
- - `Belpost::ValidationError` - ошибка валидации данных
301
- - `Belpost::ApiError` - базовая ошибка API
302
- - `Belpost::AuthenticationError` - ошибка аутентификации
303
- - `Belpost::InvalidRequestError` - ошибка запроса
304
- - `Belpost::RateLimitError` - превышен лимит запросов
305
- - `Belpost::ServerError` - ошибка сервера
306
- - `Belpost::NetworkError` - сетевая ошибка
307
- - `Belpost::TimeoutError` - таймаут запроса
315
+ - `Belpost::ConfigurationError` - configuration error
316
+ - `Belpost::ValidationError` - data validation error
317
+ - `Belpost::ApiError` - basic API error
318
+ - `Belpost::AuthenticationError` - authentication error
319
+ - `Belpost::InvalidRequestError` - request error
320
+ - `Belpost::RateLimitError` - request limit exceeded
321
+ - `Belpost::ServerError` - server error
322
+ - `Belpost::NetworkError` - network error
323
+ - `Belpost::RequestError` - request timeout
308
324
 
309
- Пример обработки ошибок:
325
+ Example of error handling:
310
326
 
311
327
  ```ruby
312
328
  begin
313
329
  client = Belpost::Client.new
314
330
  response = client.create_parcel(parcel_data)
315
331
  rescue Belpost::ValidationError => e
316
- puts "Ошибка валидации: #{e.message}"
332
+ puts "Validation error: #{e.message}"
317
333
  rescue Belpost::AuthenticationError => e
318
- puts "Ошибка аутентификации: #{e.message}"
334
+ puts "Authentication error: #{e.message}"
319
335
  rescue Belpost::ApiError => e
320
- puts "Ошибка API: #{e.message}"
336
+ puts "API error: #{e.message}"
321
337
  end
322
338
  ```
323
339
 
324
- ## Документация
340
+ ## Documentation
341
+
342
+ Full documentation on the Belpost API is available in the official documentation.
343
+
344
+ ## Development
345
+
346
+ After cloning the repository, run `bin/setup` to install dependencies. Then run `rake spec` to run tests. You can also run `bin/console` for an interactive REPL that allows you to experiment.
325
347
 
326
- Полная документация по API Белпочты доступна в официальной документации.
348
+ ### Setting up test environment
327
349
 
328
- ## Разработка
350
+ For running tests, the gem uses environment variables that can be configured in different ways:
329
351
 
330
- После клонирования репозитория выполните `bin/setup` для установки зависимостей. Затем выполните `rake spec` для запуска тестов.
352
+ 1. Copy the `.env.test.example` file to `.env.test` and adjust the values:
353
+ ```
354
+ cp .env.test.example .env.test
355
+ ```
356
+
357
+ 2. The test suite will automatically use these values, or fall back to default test values if not provided.
358
+
359
+ 3. For CI environments, the necessary environment variables are already configured in the GitHub workflow files.
360
+
361
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
362
+
363
+ ## Continuous Integration (CI/CD)
364
+
365
+ The project is set up to use GitHub Actions for continuous integration (CI) and continuous delivery (CD):
366
+
367
+ 1. **Testing**: Every push and pull request to the `master` branch automatically runs tests on various Ruby versions.
368
+ 2. **Release**: When a tag starting with `v` is created (e.g., `v0.1.0`), the gem will be automatically published to RubyGems.
369
+
370
+ For more detailed information about the release process, see the [RELEASING.md](RELEASING.md) file.
331
371
 
332
372
  ## Contributing
333
373
 
334
- Bug reports and pull requests are welcome on GitHub.
374
+ For information on how to contribute to the project, please see [CONTRIBUTING.md](CONTRIBUTING.md).
375
+
376
+ ## License
377
+
378
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/RELEASING.md ADDED
@@ -0,0 +1,41 @@
1
+ # Releasing a New Gem Version
2
+
3
+ This document describes the process for releasing a new version of the Belpost gem.
4
+
5
+ ## Release Steps
6
+
7
+ 1. Make sure all tests pass successfully:
8
+ ```
9
+ bundle exec rspec
10
+ ```
11
+
12
+ 2. Update the version number in `lib/belpost/version.rb`
13
+
14
+ 3. Update CHANGELOG.md with descriptions of changes in the new version
15
+
16
+ 4. Commit your changes:
17
+ ```
18
+ git add lib/belpost/version.rb CHANGELOG.md
19
+ git commit -m "Release version X.Y.Z"
20
+ ```
21
+
22
+ 5. Create a tag for the new version:
23
+ ```
24
+ git tag -a vX.Y.Z -m "Version X.Y.Z"
25
+ ```
26
+
27
+ 6. Push the commit and tag to GitHub:
28
+ ```
29
+ git push origin master
30
+ git push origin vX.Y.Z
31
+ ```
32
+
33
+ 7. GitHub Actions will automatically publish the gem to RubyGems.org when a new tag starting with 'v' is created.
34
+
35
+ ## Setup
36
+
37
+ For automatic gem publication, you need to add the `RUBYGEMS_API_KEY` secret in your GitHub repository settings:
38
+
39
+ 1. Create an API key on RubyGems.org (if not already created)
40
+ 2. Go to your repository settings on GitHub: Settings > Secrets and variables > Actions
41
+ 3. Add a new secret named `RUBYGEMS_API_KEY` with the value of your API key from RubyGems.org
data/Rakefile CHANGED
@@ -2,11 +2,74 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
5
  require "rubocop/rake_task"
9
6
 
7
+ RSpec::Core::RakeTask.new(:spec)
10
8
  RuboCop::RakeTask.new
11
9
 
12
10
  task default: %i[spec rubocop]
11
+
12
+ namespace :gem do
13
+ desc "Build the belpost gem"
14
+ task :build do
15
+ system "gem build belpost.gemspec"
16
+ end
17
+
18
+ desc "Install the gem locally"
19
+ task install: :build do
20
+ system "gem install belpost-*.gem"
21
+ end
22
+
23
+ desc "Clean up gem-related files"
24
+ task :clean do
25
+ system "rm -f *.gem"
26
+ end
27
+ end
28
+
29
+ namespace :version do
30
+ desc "Display current version"
31
+ task :show do
32
+ require_relative "lib/belpost/version"
33
+ puts "Current version: #{Belpost::VERSION}"
34
+ end
35
+
36
+ desc "Increment patch version (x.y.Z)"
37
+ task :patch do
38
+ update_version(:patch)
39
+ end
40
+
41
+ desc "Increment minor version (x.Y.z)"
42
+ task :minor do
43
+ update_version(:minor)
44
+ end
45
+
46
+ desc "Increment major version (X.y.z)"
47
+ task :major do
48
+ update_version(:major)
49
+ end
50
+ end
51
+
52
+ def update_version(level)
53
+ version_file = "lib/belpost/version.rb"
54
+ content = File.read(version_file)
55
+ major, minor, patch = content.match(/VERSION\s*=\s*["'](\d+)\.(\d+)\.(\d+)["']/)[1, 3].map(&:to_i)
56
+
57
+ case level
58
+ when :major
59
+ major += 1
60
+ minor = 0
61
+ patch = 0
62
+ when :minor
63
+ minor += 1
64
+ patch = 0
65
+ when :patch
66
+ patch += 1
67
+ end
68
+
69
+ new_version = "#{major}.#{minor}.#{patch}"
70
+ new_content = content.gsub(/VERSION\s*=\s*["']\d+\.\d+\.\d+["']/, "VERSION = \"#{new_version}\"")
71
+
72
+ File.write(version_file, new_content)
73
+ puts "Version updated to #{new_version}"
74
+ puts "Don't forget to update CHANGELOG.md!"
75
+ end
@@ -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.1.0"
4
+ VERSION = "0.7.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belpost
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - KuberLite
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-03-31 00:00:00.000000000 Z
10
+ date: 2025-04-01 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: dotenv
@@ -46,9 +45,13 @@ extensions: []
46
45
  extra_rdoc_files: []
47
46
  files:
48
47
  - ".env.example "
48
+ - ".env.test.example"
49
49
  - ".rspec"
50
50
  - ".rubocop.yml"
51
+ - CHANGELOG.md
52
+ - CONTRIBUTING.md
51
53
  - README.md
54
+ - RELEASING.md
52
55
  - Rakefile
53
56
  - belpost.gemspec
54
57
  - lib/belpost.rb
@@ -61,6 +64,7 @@ files:
61
64
  - lib/belpost/models/parcel.rb
62
65
  - lib/belpost/models/parcel_builder.rb
63
66
  - lib/belpost/retry.rb
67
+ - lib/belpost/validations/address_schema.rb
64
68
  - lib/belpost/validations/parcel_schema.rb
65
69
  - lib/belpost/version.rb
66
70
  homepage: https://github.com/KuberLite/belpost
@@ -72,7 +76,6 @@ metadata:
72
76
  source_code_uri: https://github.com/belpost/evropochta
73
77
  homepage_uri: https://github.com/KuberLite/belpost
74
78
  rubygems_mfa_required: 'true'
75
- post_install_message:
76
79
  rdoc_options: []
77
80
  require_paths:
78
81
  - lib
@@ -87,8 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
90
  - !ruby/object:Gem::Version
88
91
  version: '0'
89
92
  requirements: []
90
- rubygems_version: 3.5.3
91
- signing_key:
93
+ rubygems_version: 3.6.2
92
94
  specification_version: 4
93
95
  summary: Belpost API wrapper
94
96
  test_files: []