cdek_api_client 0.1.0 → 0.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.
- checksums.yaml +4 -4
- data/README.md +72 -14
- data/lib/cdek_api_client/api/location.rb +74 -0
- data/lib/cdek_api_client/api/order.rb +43 -0
- data/lib/cdek_api_client/api/tariff.rb +23 -0
- data/lib/cdek_api_client/api/track_order.rb +48 -0
- data/lib/cdek_api_client/api/webhook.rb +41 -0
- data/lib/cdek_api_client/client.rb +98 -71
- data/lib/cdek_api_client/entities/currency_mapper.rb +6 -0
- data/lib/cdek_api_client/entities/item.rb +15 -0
- data/lib/cdek_api_client/entities/location.rb +11 -0
- data/lib/cdek_api_client/entities/order_data.rb +20 -0
- data/lib/cdek_api_client/entities/package.rb +15 -0
- data/lib/cdek_api_client/entities/payment.rb +11 -1
- data/lib/cdek_api_client/entities/recipient.rb +11 -0
- data/lib/cdek_api_client/entities/sender.rb +11 -0
- data/lib/cdek_api_client/entities/service.rb +11 -0
- data/lib/cdek_api_client/entities/tariff_data.rb +15 -0
- data/lib/cdek_api_client/entities/validatable.rb +88 -7
- data/lib/cdek_api_client/entities/webhook.rb +11 -0
- data/lib/cdek_api_client/version.rb +1 -1
- data/lib/cdek_api_client.rb +41 -13
- metadata +8 -51
- data/lib/cdek_api_client/location.rb +0 -32
- data/lib/cdek_api_client/order.rb +0 -43
- data/lib/cdek_api_client/recipient.rb +0 -37
- data/lib/cdek_api_client/tariff.rb +0 -32
- data/lib/cdek_api_client/track_order.rb +0 -29
- data/lib/cdek_api_client/webhook.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb260a05ab2e01a502a9170810e6f4ad293f4250d369aa78495346ee91b41f42
|
4
|
+
data.tar.gz: 1072390e4220defd519199194b301a166caade65276204ceec4314d98e738562
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1bdbca95336d4c39bb72909e880aa8473b2a0249ccc997a504916c55944b8fe750454e1c5bd171eacfe45c47f9dc3a3b92743957a898102c39d90c8ff88ef7a
|
7
|
+
data.tar.gz: e0f39d6d3ccb456f98446974e96c1160a001fc6c9970fc9e2162efcc24cdd78aa003e2516d6256ce4de9add37634c83b3d9c7e7a125bc34fbd9a668c9647591f
|
data/README.md
CHANGED
@@ -2,12 +2,24 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/cdek_api_client)
|
4
4
|
|
5
|
-
|
5
|
+
### Other Languages
|
6
6
|
|
7
|
-
|
7
|
+
- [Русский](README_RUS.md)
|
8
|
+
- [Татарча](README_TAT.md)
|
9
|
+
- [English](README.md)
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
+
**Important:** This gem is in the early stages of development and it is shared as it is. Any support for development or feedback is welcome; please check the [Contributing](#contributing) section for more information.
|
12
|
+
|
13
|
+
## Overview
|
14
|
+
|
15
|
+
CDEK ([СДЭК](https://www.cdek.ru/)) is a big logistics company in Russia, that provides a wide range of delivery services for businesses and individuals. The [CDEK API](https://www.cdek.ru/ru/integration/api) allows developers to integrate CDEK's services into their applications, enabling functionalities such as order creation, tracking, tariff calculation, location data retrieval, and webhook management.
|
16
|
+
|
17
|
+
The `cdek_api_client` gem offers a clean and robust interface to interact with the CDEK API, ensuring maintainable code with proper validations. This gem supports the following features:
|
18
|
+
|
19
|
+
- Creating and tracking orders
|
20
|
+
- Calculating tariffs
|
21
|
+
- Retrieving location data (cities, regions, postal codes, and offices)
|
22
|
+
- Managing webhooks
|
11
23
|
|
12
24
|
## Table of Contents
|
13
25
|
|
@@ -19,12 +31,15 @@ This Readme is also available in:
|
|
19
31
|
- [Calculating Tariff](#calculating-tariff)
|
20
32
|
- [Getting Location Data](#getting-location-data)
|
21
33
|
- [Setting Up Webhooks](#setting-up-webhooks)
|
34
|
+
- [Fetching and Saving Location Data](#fetching-and-saving-location-data)
|
22
35
|
- [Entities](#entities)
|
23
36
|
- [OrderData](#orderdata)
|
24
37
|
- [Recipient](#recipient)
|
25
38
|
- [Sender](#sender)
|
26
39
|
- [Package](#package)
|
27
40
|
- [Item](#item)
|
41
|
+
- [TODO List](#todo-list)
|
42
|
+
- [Changelog](#changelog)
|
28
43
|
- [Contributing](#contributing)
|
29
44
|
- [License](#license)
|
30
45
|
|
@@ -36,8 +51,6 @@ Add this line to your application's Gemfile:
|
|
36
51
|
gem 'cdek_api_client'
|
37
52
|
```
|
38
53
|
|
39
|
-
````
|
40
|
-
|
41
54
|
And then execute:
|
42
55
|
|
43
56
|
```sh
|
@@ -222,7 +235,44 @@ rescue => e
|
|
222
235
|
end
|
223
236
|
```
|
224
237
|
|
225
|
-
|
238
|
+
### Fetching Location Data
|
239
|
+
|
240
|
+
The gem has pre-cached values and uses them by default. Users can override this behavior by fetching live data.
|
241
|
+
|
242
|
+
You can fetch cities, regions, offices, and postal code data directly from CDEK API.
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
|
246
|
+
# Fetching cities
|
247
|
+
begin
|
248
|
+
cities = location_client.cities(use_live_data: true)
|
249
|
+
rescue => e
|
250
|
+
puts "Error fetching cities: #{e.message}"
|
251
|
+
end
|
252
|
+
|
253
|
+
# Fetching regions
|
254
|
+
begin
|
255
|
+
regions = location_client.regions(use_live_data: true)
|
256
|
+
rescue => e
|
257
|
+
puts "Error fetching regions: #{e.message}"
|
258
|
+
end
|
259
|
+
|
260
|
+
# Fetching offices
|
261
|
+
begin
|
262
|
+
offices = location_client.offices(use_live_data: true)
|
263
|
+
rescue => e
|
264
|
+
puts "Error fetching offices: #{e.message}"
|
265
|
+
end
|
266
|
+
|
267
|
+
# Fetching postal codes for each city
|
268
|
+
begin
|
269
|
+
cities = location_client.cities(use_live_data: true)
|
270
|
+
rescue => e
|
271
|
+
puts "Error fetching postal codes: #{e.message}"
|
272
|
+
end
|
273
|
+
```
|
274
|
+
|
275
|
+
### Entities
|
226
276
|
|
227
277
|
### OrderData
|
228
278
|
|
@@ -237,7 +287,7 @@ Attributes:
|
|
237
287
|
- `recipient` (Recipient, required): The recipient details.
|
238
288
|
- `sender` (Sender, required): The sender details.
|
239
289
|
- `from_location` (Hash, required): The location details from where the order is shipped.
|
240
|
-
- `to_location` (Hash, required): The location details
|
290
|
+
- `to_location` (Hash, required): The location details of where the order is shipped.
|
241
291
|
- `services` (Array): Additional services.
|
242
292
|
- `packages` (Array, required): List of packages.
|
243
293
|
|
@@ -253,7 +303,7 @@ Attributes:
|
|
253
303
|
|
254
304
|
### Sender
|
255
305
|
|
256
|
-
Represents the sender details.
|
306
|
+
Represents the sender's details.
|
257
307
|
|
258
308
|
Attributes:
|
259
309
|
|
@@ -290,17 +340,25 @@ Attributes:
|
|
290
340
|
|
291
341
|
## TODO List
|
292
342
|
|
293
|
-
- [
|
294
|
-
- [
|
343
|
+
- [x] Restructure the codebase for better organization.
|
344
|
+
- [x] Add mappings for CDEK internal codes.
|
295
345
|
- [ ] Add more API endpoints and data entities.
|
296
346
|
- [ ] Check all attributes for required and optional fields.
|
297
347
|
- [ ] Add documentation for all classes and methods.
|
298
348
|
|
349
|
+
## Changelog
|
350
|
+
|
351
|
+
### v0.2.0
|
352
|
+
|
353
|
+
- **Added**: Improved error handling and response parsing in `Client`.
|
354
|
+
- **Updated**: Code structure for better organization.
|
355
|
+
- **Updated**: Specs for `Client` and `API` classes.
|
356
|
+
- **Updated**: `README.md` with detailed usage examples, including fetching and saving location data with optional live data fetching.
|
357
|
+
|
299
358
|
## Contributing
|
300
359
|
|
301
|
-
Bug reports and pull requests are welcome on GitHub
|
360
|
+
Bug reports and pull requests are welcome on GitHub. This gem is used in a couple of projects and fulfills the requirements of those projects. If you have any suggestions or improvements, feel free to open an issue or a pull request.
|
302
361
|
|
303
362
|
## License
|
304
363
|
|
305
|
-
The gem is available as open
|
306
|
-
````
|
364
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module CDEKApiClient
|
6
|
+
module API
|
7
|
+
# Handles location-related API requests.
|
8
|
+
class Location
|
9
|
+
# Initializes the Location object.
|
10
|
+
#
|
11
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
12
|
+
def initialize(client)
|
13
|
+
@client = client
|
14
|
+
end
|
15
|
+
|
16
|
+
# Retrieves a list of cities.
|
17
|
+
#
|
18
|
+
# @param use_live_data [Boolean] whether to use live data or cached data.
|
19
|
+
# @return [Array<Hash>] list of cities.
|
20
|
+
def cities(use_live_data: false)
|
21
|
+
use_live_data ? @client.request('get', 'location/cities') : read_data_from_file('cities_mapping.json')
|
22
|
+
end
|
23
|
+
|
24
|
+
# Retrieves a list of regions.
|
25
|
+
#
|
26
|
+
# @param use_live_data [Boolean] whether to use live data or cached data.
|
27
|
+
# @return [Array<Hash>] list of regions.
|
28
|
+
def regions(use_live_data: false)
|
29
|
+
use_live_data ? @client.request('get', 'location/regions') : read_data_from_file('regions_mapping.json')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Retrieves a list of offices.
|
33
|
+
#
|
34
|
+
# @param use_live_data [Boolean] whether to use live data or cached data.
|
35
|
+
# @return [Array<Hash>] list of offices.
|
36
|
+
def offices(use_live_data: false)
|
37
|
+
use_live_data ? @client.request('get', 'deliverypoints') : read_data_from_file('offices_mapping.json')
|
38
|
+
end
|
39
|
+
|
40
|
+
# Retrieves a list of postal codes.
|
41
|
+
#
|
42
|
+
# @param city_code [String, nil] the city code to filter postal codes.
|
43
|
+
# @param use_live_data [Boolean] whether to use live data or cached data.
|
44
|
+
# @return [Array<Hash>] list of postal codes.
|
45
|
+
def postal_codes(city_code = nil, use_live_data: false)
|
46
|
+
if use_live_data
|
47
|
+
if city_code
|
48
|
+
@client.request('get',
|
49
|
+
"location/postalcodes?code=#{city_code}")
|
50
|
+
else
|
51
|
+
@client.request('get',
|
52
|
+
'location/postalcodes')
|
53
|
+
end
|
54
|
+
else
|
55
|
+
read_data_from_file('postal_codes_mapping.json')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Reads data from a file.
|
62
|
+
#
|
63
|
+
# @param filename [String] the name of the file to read.
|
64
|
+
# @return [Hash] the parsed JSON data from the file.
|
65
|
+
def read_data_from_file(filename)
|
66
|
+
file_path = File.join('data', filename)
|
67
|
+
JSON.parse(File.read(file_path))
|
68
|
+
rescue StandardError => e
|
69
|
+
@client.logger.error("Failed to read data from file: #{e.message}")
|
70
|
+
{ 'error' => e.message }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDEKApiClient
|
4
|
+
module API
|
5
|
+
# Handles order-related API requests.
|
6
|
+
class Order
|
7
|
+
# Initializes the Order object.
|
8
|
+
#
|
9
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a new order.
|
15
|
+
#
|
16
|
+
# @param order_data [CDEKApiClient::Entities::OrderData] the data for the order.
|
17
|
+
# @return [Hash] the response from the API.
|
18
|
+
def create(order_data)
|
19
|
+
response = @client.request('post', 'orders', body: order_data)
|
20
|
+
handle_response(response)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Tracks an order by its UUID.
|
24
|
+
#
|
25
|
+
# @param order_uuid [String] the UUID of the order.
|
26
|
+
# @return [Hash] the tracking information.
|
27
|
+
def track(order_uuid)
|
28
|
+
response = @client.request('get', "orders/#{order_uuid}")
|
29
|
+
handle_response(response)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Handles the response from the API.
|
35
|
+
#
|
36
|
+
# @param response [Net::HTTPResponse] the response from the API.
|
37
|
+
# @return [Hash] the parsed response.
|
38
|
+
def handle_response(response)
|
39
|
+
@client.send(:handle_response, response)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDEKApiClient
|
4
|
+
module API
|
5
|
+
# Handles tariff-related API requests.
|
6
|
+
class Tariff
|
7
|
+
# Initializes the Tariff object.
|
8
|
+
#
|
9
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Calculates the tariff.
|
15
|
+
#
|
16
|
+
# @param tariff_data [CDEKApiClient::Entities::TariffData] the data for the tariff calculation.
|
17
|
+
# @return [Hash] the response from the API.
|
18
|
+
def calculate(tariff_data)
|
19
|
+
@client.request('post', 'calculator/tariff', body: tariff_data)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDEKApiClient
|
4
|
+
module API
|
5
|
+
# Handles order tracking requests to the CDEK API.
|
6
|
+
class TrackOrder
|
7
|
+
BASE_URL = ENV.fetch('CDEK_API_URL', 'https://api.edu.cdek.ru/v2')
|
8
|
+
TRACK_ORDER_URL = "#{BASE_URL}/orders/%<uuid>s".freeze
|
9
|
+
|
10
|
+
# Initializes the TrackOrder object.
|
11
|
+
#
|
12
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
13
|
+
def initialize(client)
|
14
|
+
@client = client
|
15
|
+
end
|
16
|
+
|
17
|
+
# Retrieves tracking information for an order.
|
18
|
+
#
|
19
|
+
# @param order_uuid [String] the UUID of the order.
|
20
|
+
# @return [Hash] the tracking information.
|
21
|
+
# @raise [ArgumentError] if the UUID is invalid.
|
22
|
+
def get(order_uuid)
|
23
|
+
validate_uuid(order_uuid)
|
24
|
+
|
25
|
+
response = @client.auth_connection.get(format(TRACK_ORDER_URL, uuid: order_uuid))
|
26
|
+
handle_response(response)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Validates the order UUID.
|
32
|
+
#
|
33
|
+
# @param uuid [String] the UUID to validate.
|
34
|
+
# @raise [ArgumentError] if the UUID is invalid.
|
35
|
+
def validate_uuid(uuid)
|
36
|
+
@client.send(:validate_uuid, uuid)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Handles the response from the API.
|
40
|
+
#
|
41
|
+
# @param response [Net::HTTPResponse] the response from the API.
|
42
|
+
# @return [Hash] the parsed response.
|
43
|
+
def handle_response(response)
|
44
|
+
@client.send(:handle_response, response)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CDEKApiClient
|
4
|
+
module API
|
5
|
+
# Handles webhook-related API requests.
|
6
|
+
class Webhook
|
7
|
+
# Initializes the Webhook object.
|
8
|
+
#
|
9
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
end
|
13
|
+
|
14
|
+
# Registers a new webhook.
|
15
|
+
#
|
16
|
+
# @param webhook_data [CDEKApiClient::Entities::Webhook] the data for the webhook.
|
17
|
+
# @return [Hash] the response from the API.
|
18
|
+
def register(webhook_data)
|
19
|
+
response = @client.request('post', 'webhooks', body: webhook_data)
|
20
|
+
@client.send(:handle_response, response)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Retrieves a list of registered webhooks.
|
24
|
+
#
|
25
|
+
# @return [Array<Hash>] the list of webhooks.
|
26
|
+
def list
|
27
|
+
response = @client.request('get', 'webhooks')
|
28
|
+
@client.send(:handle_response, response)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Deletes a webhook by its ID.
|
32
|
+
#
|
33
|
+
# @param webhook_id [String] the ID of the webhook to delete.
|
34
|
+
# @return [Hash] the response from the API.
|
35
|
+
def delete(webhook_id)
|
36
|
+
response = @client.request('delete', "webhooks/#{webhook_id}")
|
37
|
+
@client.send(:handle_response, response)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,114 +1,141 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require '
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
5
|
require 'json'
|
6
6
|
require 'logger'
|
7
|
+
require_relative 'api/location'
|
8
|
+
require_relative 'api/order'
|
9
|
+
require_relative 'api/tariff'
|
10
|
+
require_relative 'api/webhook'
|
7
11
|
|
8
12
|
module CDEKApiClient
|
9
|
-
##
|
10
13
|
# Client class for interacting with the CDEK API.
|
11
|
-
#
|
12
|
-
# This class provides methods for authentication and initializing the API resources
|
13
|
-
# for orders, locations, tariffs, and webhooks.
|
14
|
-
#
|
15
|
-
# @attr_reader [String] token The access token used for authentication.
|
16
|
-
# @attr_reader [Logger] logger The logger used for logging HTTP requests and responses.
|
17
|
-
# @attr_reader [Order] order The Order resource for interacting with order-related API endpoints.
|
18
|
-
# @attr_reader [Location] location The Location resource for interacting with location-related API endpoints.
|
19
|
-
# @attr_reader [Tariff] tariff The Tariff resource for interacting with tariff-related API endpoints.
|
20
|
-
# @attr_reader [Webhook] webhook The Webhook resource for interacting with webhook-related API endpoints.
|
21
14
|
class Client
|
22
15
|
BASE_URL = ENV.fetch('CDEK_API_URL', 'https://api.edu.cdek.ru/v2')
|
23
16
|
TOKEN_URL = "#{BASE_URL}/oauth/token".freeze
|
24
17
|
|
25
|
-
|
18
|
+
# @return [String] the access token for API authentication.
|
19
|
+
attr_reader :token
|
20
|
+
# @return [Logger] the logger instance.
|
21
|
+
attr_reader :logger
|
22
|
+
# @return [CDEKApiClient::Order] the order API interface.
|
23
|
+
attr_reader :order
|
24
|
+
# @return [CDEKApiClient::Location] the location API interface.
|
25
|
+
attr_reader :location
|
26
|
+
# @return [CDEKApiClient::Tariff] the tariff API interface.
|
27
|
+
attr_reader :tariff
|
28
|
+
# @return [CDEKApiClient::Webhook] the webhook API interface.
|
29
|
+
attr_reader :webhook
|
26
30
|
|
27
|
-
|
28
|
-
# Initializes a new Client object.
|
31
|
+
# Initializes the client with API credentials and logger.
|
29
32
|
#
|
30
|
-
# @param [String]
|
31
|
-
# @param [String]
|
32
|
-
# @param [Logger]
|
33
|
+
# @param client_id [String] the client ID.
|
34
|
+
# @param client_secret [String] the client secret.
|
35
|
+
# @param logger [Logger] the logger instance.
|
33
36
|
def initialize(client_id, client_secret, logger: Logger.new($stdout))
|
34
37
|
@client_id = client_id
|
35
38
|
@client_secret = client_secret
|
36
39
|
@logger = logger
|
37
40
|
@token = authenticate
|
38
41
|
|
39
|
-
@order = CDEKApiClient::Order.new(self)
|
40
|
-
@location = CDEKApiClient::Location.new(self)
|
41
|
-
@tariff = CDEKApiClient::Tariff.new(self)
|
42
|
-
@webhook = CDEKApiClient::Webhook.new(self)
|
42
|
+
@order = CDEKApiClient::API::Order.new(self)
|
43
|
+
@location = CDEKApiClient::API::Location.new(self)
|
44
|
+
@tariff = CDEKApiClient::API::Tariff.new(self)
|
45
|
+
@webhook = CDEKApiClient::API::Webhook.new(self)
|
43
46
|
end
|
44
47
|
|
45
|
-
|
46
|
-
# Authenticates with the CDEK API and retrieves an access token.
|
48
|
+
# Authenticates with the API and retrieves an access token.
|
47
49
|
#
|
48
|
-
# @return [String]
|
49
|
-
# @raise [
|
50
|
+
# @return [String] the access token.
|
51
|
+
# @raise [StandardError] if authentication fails.
|
50
52
|
def authenticate
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
53
|
+
uri = URI(TOKEN_URL)
|
54
|
+
response = Net::HTTP.post_form(uri, {
|
55
|
+
grant_type: 'client_credentials',
|
56
|
+
client_id: @client_id,
|
57
|
+
client_secret: @client_secret
|
58
|
+
})
|
58
59
|
|
59
|
-
raise
|
60
|
+
raise "Error getting token: #{response.body}" unless response.is_a?(Net::HTTPSuccess)
|
60
61
|
|
61
|
-
response.body['access_token']
|
62
|
+
JSON.parse(response.body)['access_token']
|
62
63
|
end
|
63
64
|
|
64
|
-
|
65
|
-
# Creates a Faraday connection object.
|
65
|
+
# Makes an HTTP request to the API.
|
66
66
|
#
|
67
|
-
# @
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
67
|
+
# @param method [String] the HTTP method (e.g., 'get', 'post').
|
68
|
+
# @param path [String] the API endpoint path.
|
69
|
+
# @param body [Hash, nil] the request body.
|
70
|
+
# @return [Hash, Array] the parsed response.
|
71
|
+
def request(method, path, body: nil)
|
72
|
+
uri = URI("#{BASE_URL}/#{path}")
|
73
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
74
|
+
http.use_ssl = true
|
75
|
+
request = build_request(method, uri, body)
|
76
|
+
response = http.request(request)
|
77
|
+
handle_response(response)
|
78
|
+
rescue StandardError => e
|
79
|
+
@logger.error("HTTP request failed: #{e.message}")
|
80
|
+
{ 'error' => e.message }
|
75
81
|
end
|
76
82
|
|
77
|
-
|
78
|
-
|
83
|
+
private
|
84
|
+
|
85
|
+
# Builds an HTTP request with the specified method, URI, and body.
|
79
86
|
#
|
80
|
-
# @
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
87
|
+
# @param method [String] the HTTP method (e.g., 'get', 'post').
|
88
|
+
# @param uri [URI::HTTP] the URI for the request.
|
89
|
+
# @param body [Hash, nil] the request body.
|
90
|
+
# @return [Net::HTTPRequest] the constructed HTTP request.
|
91
|
+
def build_request(method, uri, body)
|
92
|
+
request_class = Net::HTTP.const_get(method.capitalize)
|
93
|
+
request = request_class.new(uri.request_uri)
|
94
|
+
request['Authorization'] = "Bearer #{@token}"
|
95
|
+
request['Content-Type'] = 'application/json'
|
96
|
+
request.body = body.to_json if body
|
97
|
+
request
|
89
98
|
end
|
90
99
|
|
91
|
-
|
92
|
-
# Handles the response from the API.
|
100
|
+
# Handles the API response, parsing JSON and handling errors.
|
93
101
|
#
|
94
|
-
# @param [
|
95
|
-
# @return [Hash]
|
96
|
-
# @raise [Error] if the response is not successful.
|
102
|
+
# @param response [Net::HTTPResponse] the HTTP response.
|
103
|
+
# @return [Hash, Array] the parsed response.
|
97
104
|
def handle_response(response)
|
98
|
-
|
105
|
+
case response
|
106
|
+
when Net::HTTPSuccess
|
107
|
+
parsed_response = parse_json(response.body)
|
108
|
+
return parsed_response unless parsed_response.is_a?(Hash) && parsed_response.key?('error')
|
99
109
|
|
100
|
-
|
110
|
+
log_error("API Error: #{parsed_response['error']}")
|
111
|
+
{ 'error' => parsed_response['error'] }
|
112
|
+
when Array, Hash
|
113
|
+
response
|
114
|
+
else
|
115
|
+
log_error("Unexpected response type: #{response.class}")
|
116
|
+
{ 'error' => "Unexpected response type: #{response.class}" }
|
117
|
+
end
|
118
|
+
rescue JSON::ParserError => e
|
119
|
+
log_error("Failed to parse response: #{e.message}")
|
120
|
+
{ 'error' => "Failed to parse response: #{e.message}" }
|
101
121
|
end
|
102
122
|
|
103
|
-
|
104
|
-
# Validates the format of a UUID.
|
123
|
+
# Parses a JSON string, handling any parsing errors.
|
105
124
|
#
|
106
|
-
# @param [String]
|
107
|
-
# @
|
108
|
-
def
|
109
|
-
|
125
|
+
# @param body [String] the JSON string to parse.
|
126
|
+
# @return [Hash] the parsed JSON.
|
127
|
+
def parse_json(body)
|
128
|
+
JSON.parse(body)
|
129
|
+
rescue JSON::ParserError => e
|
130
|
+
log_error("Failed to parse JSON body: #{e.message}")
|
131
|
+
{ 'error' => "Failed to parse JSON body: #{e.message}" }
|
132
|
+
end
|
110
133
|
|
111
|
-
|
134
|
+
# Logs an error message.
|
135
|
+
#
|
136
|
+
# @param message [String] the error message to log.
|
137
|
+
def log_error(message)
|
138
|
+
@logger.error(message)
|
112
139
|
end
|
113
140
|
end
|
114
141
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module CDEKApiClient
|
4
4
|
module Entities
|
5
|
+
# CurrencyMapper is a utility module that maps currency codes to their respective integer representations.
|
5
6
|
module CurrencyMapper
|
6
7
|
CURRENCY_CODES = {
|
7
8
|
'RUB' => 1,
|
@@ -26,6 +27,11 @@ module CDEKApiClient
|
|
26
27
|
'JPY' => 55
|
27
28
|
}.freeze
|
28
29
|
|
30
|
+
# Converts a currency code to its corresponding integer representation.
|
31
|
+
#
|
32
|
+
# @param currency [String] the currency code to convert.
|
33
|
+
# @return [Integer] the integer representation of the currency code.
|
34
|
+
# @raise [ArgumentError] if the currency code is invalid.
|
29
35
|
def self.to_code(currency)
|
30
36
|
CURRENCY_CODES[currency] || (raise ArgumentError, "Invalid currency code: #{currency}")
|
31
37
|
end
|
@@ -5,6 +5,8 @@ require_relative 'payment'
|
|
5
5
|
|
6
6
|
module CDEKApiClient
|
7
7
|
module Entities
|
8
|
+
# Represents an item in the CDEK API.
|
9
|
+
# Each item has attributes such as ware key, payment, name, cost, amount, weight, and optional URL.
|
8
10
|
class Item
|
9
11
|
include Validatable
|
10
12
|
|
@@ -17,6 +19,16 @@ module CDEKApiClient
|
|
17
19
|
validates :amount, type: :integer, presence: true
|
18
20
|
validates :weight, type: :integer, presence: true
|
19
21
|
|
22
|
+
# Initializes a new Item object.
|
23
|
+
#
|
24
|
+
# @param ware_key [String] the ware key of the item.
|
25
|
+
# @param payment [Payment] the payment details of the item.
|
26
|
+
# @param name [String] the name of the item.
|
27
|
+
# @param cost [Integer] the cost of the item.
|
28
|
+
# @param amount [Integer] the amount of the item.
|
29
|
+
# @param weight [Integer] the weight of the item.
|
30
|
+
# @param url [String, nil] the optional URL of the item.
|
31
|
+
# @raise [ArgumentError] if any attribute validation fails.
|
20
32
|
def initialize(ware_key:, payment:, name:, cost:, amount:, weight:, url: nil)
|
21
33
|
@ware_key = ware_key
|
22
34
|
@payment = payment
|
@@ -28,6 +40,9 @@ module CDEKApiClient
|
|
28
40
|
validate!
|
29
41
|
end
|
30
42
|
|
43
|
+
# Converts the Item object to a JSON representation.
|
44
|
+
#
|
45
|
+
# @return [String] the JSON representation of the Item.
|
31
46
|
def to_json(*_args)
|
32
47
|
{
|
33
48
|
ware_key: @ware_key,
|