cdek_api_client 0.2.0 → 0.3.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/CHANGELOG.md +77 -0
- data/README.md +373 -13
- data/lib/cdek_api_client/api/courier.rb +104 -0
- data/lib/cdek_api_client/api/location.rb +14 -14
- data/lib/cdek_api_client/api/order.rb +55 -0
- data/lib/cdek_api_client/api/payment.rb +52 -0
- data/lib/cdek_api_client/api/print.rb +92 -0
- data/lib/cdek_api_client/api/tariff.rb +9 -0
- data/lib/cdek_api_client/api/track_order.rb +2 -2
- data/lib/cdek_api_client/api/webhook.rb +36 -4
- data/lib/cdek_api_client/client.rb +78 -19
- data/lib/cdek_api_client/config.rb +39 -0
- data/lib/cdek_api_client/entities/agreement.rb +59 -0
- data/lib/cdek_api_client/entities/auth_error_response.rb +38 -0
- data/lib/cdek_api_client/entities/auth_response.rb +50 -0
- data/lib/cdek_api_client/entities/barcode.rb +71 -0
- data/lib/cdek_api_client/entities/check.rb +55 -0
- data/lib/cdek_api_client/entities/currency_mapper.rb +5 -2
- data/lib/cdek_api_client/entities/intake_available_days_request.rb +37 -0
- data/lib/cdek_api_client/entities/intake_available_days_response.rb +46 -0
- data/lib/cdek_api_client/entities/intakes.rb +96 -0
- data/lib/cdek_api_client/entities/invoice.rb +63 -0
- data/lib/cdek_api_client/entities/validatable.rb +3 -1
- data/lib/cdek_api_client/version.rb +1 -1
- data/lib/cdek_api_client.rb +12 -0
- metadata +24 -11
|
@@ -29,8 +29,63 @@ module CDEKApiClient
|
|
|
29
29
|
handle_response(response)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# Deletes an order by its UUID.
|
|
33
|
+
#
|
|
34
|
+
# @param order_uuid [String] the UUID of the order to delete.
|
|
35
|
+
# @return [Hash] the response from the API.
|
|
36
|
+
def delete(order_uuid)
|
|
37
|
+
validate_uuid(order_uuid)
|
|
38
|
+
response = @client.request('delete', "orders/#{order_uuid}")
|
|
39
|
+
handle_response(response)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Cancels an order by its UUID.
|
|
43
|
+
#
|
|
44
|
+
# @param order_uuid [String] the UUID of the order to cancel.
|
|
45
|
+
# @return [Hash] the response from the API.
|
|
46
|
+
def cancel(order_uuid)
|
|
47
|
+
validate_uuid(order_uuid)
|
|
48
|
+
response = @client.request('post', "orders/#{order_uuid}/refusal")
|
|
49
|
+
handle_response(response)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Updates an existing order.
|
|
53
|
+
#
|
|
54
|
+
# @param order_data [CDEKApiClient::Entities::OrderData] the updated order data.
|
|
55
|
+
# @return [Hash] the response from the API.
|
|
56
|
+
def update(order_data)
|
|
57
|
+
response = @client.request('patch', 'orders', body: order_data)
|
|
58
|
+
handle_response(response)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Gets order information by CDEK number.
|
|
62
|
+
#
|
|
63
|
+
# @param cdek_number [String] the CDEK order number.
|
|
64
|
+
# @return [Hash] the order information.
|
|
65
|
+
def get_by_cdek_number(cdek_number)
|
|
66
|
+
response = @client.request('get', 'orders', query: { cdek_number: cdek_number })
|
|
67
|
+
handle_response(response)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Gets order information by IM number.
|
|
71
|
+
#
|
|
72
|
+
# @param im_number [String] the IM order number.
|
|
73
|
+
# @return [Hash] the order information.
|
|
74
|
+
def get_by_im_number(im_number)
|
|
75
|
+
response = @client.request('get', 'orders', query: { im_number: im_number })
|
|
76
|
+
handle_response(response)
|
|
77
|
+
end
|
|
78
|
+
|
|
32
79
|
private
|
|
33
80
|
|
|
81
|
+
# Validates UUID format.
|
|
82
|
+
#
|
|
83
|
+
# @param uuid [String] the UUID to validate.
|
|
84
|
+
# @raise [ArgumentError] if the UUID format is invalid.
|
|
85
|
+
def validate_uuid(uuid)
|
|
86
|
+
@client.validate_uuid(uuid)
|
|
87
|
+
end
|
|
88
|
+
|
|
34
89
|
# Handles the response from the API.
|
|
35
90
|
#
|
|
36
91
|
# @param response [Net::HTTPResponse] the response from the API.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CDEKApiClient
|
|
4
|
+
module API
|
|
5
|
+
# Handles payment and registry-related API requests.
|
|
6
|
+
class Payment
|
|
7
|
+
# Initializes the Payment object.
|
|
8
|
+
#
|
|
9
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
|
10
|
+
def initialize(client)
|
|
11
|
+
@client = client
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Gets payment information for a specific date.
|
|
15
|
+
#
|
|
16
|
+
# @param date [String] the date in YYYY-MM-DD format.
|
|
17
|
+
# @return [Hash] the payment information.
|
|
18
|
+
def get_payments(date)
|
|
19
|
+
response = @client.request('get', 'payment', query: { date: date })
|
|
20
|
+
handle_response(response)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Gets check information.
|
|
24
|
+
#
|
|
25
|
+
# @param check_data [CDEKApiClient::Entities::Check] the check data.
|
|
26
|
+
# @return [Hash] the check information.
|
|
27
|
+
def get_checks(check_data)
|
|
28
|
+
response = @client.request('get', 'check', query: check_data)
|
|
29
|
+
handle_response(response)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Gets registry information for a specific date.
|
|
33
|
+
#
|
|
34
|
+
# @param date [String] the date in YYYY-MM-DD format.
|
|
35
|
+
# @return [Hash] the registry information.
|
|
36
|
+
def get_registries(date)
|
|
37
|
+
response = @client.request('get', 'registries', query: { date: date })
|
|
38
|
+
handle_response(response)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
# Handles the response from the API.
|
|
44
|
+
#
|
|
45
|
+
# @param response [Net::HTTPResponse] the response from the API.
|
|
46
|
+
# @return [Hash] the parsed response.
|
|
47
|
+
def handle_response(response)
|
|
48
|
+
@client.send(:handle_response, response)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CDEKApiClient
|
|
4
|
+
module API
|
|
5
|
+
# Handles print-related API requests (barcodes and invoices).
|
|
6
|
+
class Print
|
|
7
|
+
# Initializes the Print object.
|
|
8
|
+
#
|
|
9
|
+
# @param client [CDEKApiClient::Client] the client instance.
|
|
10
|
+
def initialize(client)
|
|
11
|
+
@client = client
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Creates a barcode for an order.
|
|
15
|
+
#
|
|
16
|
+
# @param barcode_data [CDEKApiClient::Entities::Barcode] the barcode data.
|
|
17
|
+
# @return [Hash] the response from the API.
|
|
18
|
+
def create_barcode(barcode_data)
|
|
19
|
+
response = @client.request('post', 'print/barcodes', body: barcode_data)
|
|
20
|
+
handle_response(response)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Gets barcode information by UUID.
|
|
24
|
+
#
|
|
25
|
+
# @param barcode_uuid [String] the UUID of the barcode.
|
|
26
|
+
# @return [Hash] the barcode information.
|
|
27
|
+
def get_barcode(barcode_uuid)
|
|
28
|
+
response = @client.request('get', "print/barcodes/#{barcode_uuid}")
|
|
29
|
+
handle_response(response)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Gets barcode PDF by UUID.
|
|
33
|
+
#
|
|
34
|
+
# @param barcode_uuid [String] the UUID of the barcode.
|
|
35
|
+
# @return [String] the PDF content.
|
|
36
|
+
def get_barcode_pdf(barcode_uuid)
|
|
37
|
+
response = @client.request('get', "print/barcodes/#{barcode_uuid}.pdf")
|
|
38
|
+
handle_binary_response(response)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Creates an invoice for an order.
|
|
42
|
+
#
|
|
43
|
+
# @param invoice_data [CDEKApiClient::Entities::Invoice] the invoice data.
|
|
44
|
+
# @return [Hash] the response from the API.
|
|
45
|
+
def create_invoice(invoice_data)
|
|
46
|
+
response = @client.request('post', 'print/orders', body: invoice_data)
|
|
47
|
+
handle_response(response)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Gets invoice information by UUID.
|
|
51
|
+
#
|
|
52
|
+
# @param invoice_uuid [String] the UUID of the invoice.
|
|
53
|
+
# @return [Hash] the invoice information.
|
|
54
|
+
def get_invoice(invoice_uuid)
|
|
55
|
+
response = @client.request('get', "print/orders/#{invoice_uuid}")
|
|
56
|
+
handle_response(response)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Gets invoice PDF by UUID.
|
|
60
|
+
#
|
|
61
|
+
# @param invoice_uuid [String] the UUID of the invoice.
|
|
62
|
+
# @return [String] the PDF content.
|
|
63
|
+
def get_invoice_pdf(invoice_uuid)
|
|
64
|
+
response = @client.request('get', "print/orders/#{invoice_uuid}.pdf")
|
|
65
|
+
handle_binary_response(response)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
# Handles the response from the API.
|
|
71
|
+
#
|
|
72
|
+
# @param response [Net::HTTPResponse] the response from the API.
|
|
73
|
+
# @return [Hash] the parsed response.
|
|
74
|
+
def handle_response(response)
|
|
75
|
+
@client.send(:handle_response, response)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Handles binary responses (PDF files).
|
|
79
|
+
#
|
|
80
|
+
# @param response [Net::HTTPResponse] the response from the API.
|
|
81
|
+
# @return [String] the binary content.
|
|
82
|
+
def handle_binary_response(response)
|
|
83
|
+
case response
|
|
84
|
+
when Net::HTTPSuccess
|
|
85
|
+
response.body
|
|
86
|
+
else
|
|
87
|
+
handle_response(response)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -18,6 +18,15 @@ module CDEKApiClient
|
|
|
18
18
|
def calculate(tariff_data)
|
|
19
19
|
@client.request('post', 'calculator/tariff', body: tariff_data)
|
|
20
20
|
end
|
|
21
|
+
|
|
22
|
+
# Calculates all available tariffs.
|
|
23
|
+
#
|
|
24
|
+
# @param tariff_data [CDEKApiClient::Entities::TariffData] the data for the tariff calculation.
|
|
25
|
+
# @return [Array<Hash>] the list of available tariffs.
|
|
26
|
+
def calculate_list(tariff_data)
|
|
27
|
+
response = @client.request('post', 'calculator/tarifflist', body: tariff_data)
|
|
28
|
+
response.is_a?(Hash) && response.key?('tariff_codes') ? response['tariff_codes'] : response
|
|
29
|
+
end
|
|
21
30
|
end
|
|
22
31
|
end
|
|
23
32
|
end
|
|
@@ -22,7 +22,7 @@ module CDEKApiClient
|
|
|
22
22
|
def get(order_uuid)
|
|
23
23
|
validate_uuid(order_uuid)
|
|
24
24
|
|
|
25
|
-
response = @client.
|
|
25
|
+
response = @client.request('get', "orders/#{order_uuid}")
|
|
26
26
|
handle_response(response)
|
|
27
27
|
end
|
|
28
28
|
|
|
@@ -33,7 +33,7 @@ module CDEKApiClient
|
|
|
33
33
|
# @param uuid [String] the UUID to validate.
|
|
34
34
|
# @raise [ArgumentError] if the UUID is invalid.
|
|
35
35
|
def validate_uuid(uuid)
|
|
36
|
-
@client.
|
|
36
|
+
@client.validate_uuid(uuid)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# Handles the response from the API.
|
|
@@ -20,6 +20,21 @@ module CDEKApiClient
|
|
|
20
20
|
@client.send(:handle_response, response)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# Registers a new webhook with simple parameters.
|
|
24
|
+
#
|
|
25
|
+
# @param url [String] the URL where the webhook will send data.
|
|
26
|
+
# @param event_types [Array<String>] the list of event types for the webhook.
|
|
27
|
+
# @param type [String] the type of webhook (default: 'WEBHOOK').
|
|
28
|
+
# @return [Hash] the response from the API.
|
|
29
|
+
def register_simple(url, event_types, type: 'WEBHOOK')
|
|
30
|
+
webhook_data = CDEKApiClient::Entities::Webhook.new(
|
|
31
|
+
url: url,
|
|
32
|
+
type: type,
|
|
33
|
+
event_types: event_types
|
|
34
|
+
)
|
|
35
|
+
register(webhook_data)
|
|
36
|
+
end
|
|
37
|
+
|
|
23
38
|
# Retrieves a list of registered webhooks.
|
|
24
39
|
#
|
|
25
40
|
# @return [Array<Hash>] the list of webhooks.
|
|
@@ -28,12 +43,29 @@ module CDEKApiClient
|
|
|
28
43
|
@client.send(:handle_response, response)
|
|
29
44
|
end
|
|
30
45
|
|
|
31
|
-
#
|
|
46
|
+
# Retrieves a list of all registered webhooks.
|
|
47
|
+
#
|
|
48
|
+
# @return [Array<Hash>] the list of webhooks.
|
|
49
|
+
def list_all
|
|
50
|
+
response = @client.request('get', 'webhooks')
|
|
51
|
+
@client.send(:handle_response, response)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Retrieves information about a specific webhook by its UUID.
|
|
55
|
+
#
|
|
56
|
+
# @param webhook_uuid [String] the UUID of the webhook.
|
|
57
|
+
# @return [Hash] the webhook information.
|
|
58
|
+
def get(webhook_uuid)
|
|
59
|
+
response = @client.request('get', "webhooks/#{webhook_uuid}")
|
|
60
|
+
@client.send(:handle_response, response)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Deletes a webhook by its UUID.
|
|
32
64
|
#
|
|
33
|
-
# @param
|
|
65
|
+
# @param webhook_uuid [String] the UUID of the webhook to delete.
|
|
34
66
|
# @return [Hash] the response from the API.
|
|
35
|
-
def delete(
|
|
36
|
-
response = @client.request('delete', "webhooks/#{
|
|
67
|
+
def delete(webhook_uuid)
|
|
68
|
+
response = @client.request('delete', "webhooks/#{webhook_uuid}")
|
|
37
69
|
@client.send(:handle_response, response)
|
|
38
70
|
end
|
|
39
71
|
end
|
|
@@ -4,43 +4,62 @@ require 'net/http'
|
|
|
4
4
|
require 'uri'
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'logger'
|
|
7
|
+
require_relative 'config'
|
|
8
|
+
require_relative 'api/courier'
|
|
7
9
|
require_relative 'api/location'
|
|
8
10
|
require_relative 'api/order'
|
|
11
|
+
require_relative 'api/payment'
|
|
12
|
+
require_relative 'api/print'
|
|
9
13
|
require_relative 'api/tariff'
|
|
10
14
|
require_relative 'api/webhook'
|
|
15
|
+
require_relative 'entities/auth_response'
|
|
16
|
+
require_relative 'entities/auth_error_response'
|
|
11
17
|
|
|
12
18
|
module CDEKApiClient
|
|
13
19
|
# Client class for interacting with the CDEK API.
|
|
14
20
|
class Client
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
# @return [String] the base API URL
|
|
22
|
+
attr_reader :base_url
|
|
18
23
|
# @return [String] the access token for API authentication.
|
|
19
24
|
attr_reader :token
|
|
20
25
|
# @return [Logger] the logger instance.
|
|
21
26
|
attr_reader :logger
|
|
22
|
-
# @return [CDEKApiClient::
|
|
23
|
-
attr_reader :
|
|
27
|
+
# @return [CDEKApiClient::Courier] the courier API interface.
|
|
28
|
+
attr_reader :courier
|
|
24
29
|
# @return [CDEKApiClient::Location] the location API interface.
|
|
25
30
|
attr_reader :location
|
|
31
|
+
# @return [CDEKApiClient::Order] the order API interface.
|
|
32
|
+
attr_reader :order
|
|
33
|
+
# @return [CDEKApiClient::Payment] the payment API interface.
|
|
34
|
+
attr_reader :payment
|
|
35
|
+
# @return [CDEKApiClient::Print] the print API interface.
|
|
36
|
+
attr_reader :print
|
|
26
37
|
# @return [CDEKApiClient::Tariff] the tariff API interface.
|
|
27
38
|
attr_reader :tariff
|
|
28
39
|
# @return [CDEKApiClient::Webhook] the webhook API interface.
|
|
29
40
|
attr_reader :webhook
|
|
30
41
|
|
|
31
|
-
# Initializes the client with API credentials and
|
|
42
|
+
# Initializes the client with API credentials and configuration.
|
|
32
43
|
#
|
|
33
44
|
# @param client_id [String] the client ID.
|
|
34
45
|
# @param client_secret [String] the client secret.
|
|
46
|
+
# @param environment [Symbol, String] the API environment (:production or :demo).
|
|
47
|
+
# Defaults to :demo or value from CDEK_API_ENV environment variable.
|
|
48
|
+
# @param base_url [String] custom API base URL (overrides environment).
|
|
49
|
+
# Defaults to value from CDEK_API_URL environment variable or environment default.
|
|
35
50
|
# @param logger [Logger] the logger instance.
|
|
36
|
-
def initialize(client_id, client_secret, logger: Logger.new($stdout))
|
|
51
|
+
def initialize(client_id, client_secret, environment: nil, base_url: nil, logger: Logger.new($stdout))
|
|
37
52
|
@client_id = client_id
|
|
38
53
|
@client_secret = client_secret
|
|
54
|
+
@base_url = Config.base_url(environment: environment, custom_url: base_url)
|
|
39
55
|
@logger = logger
|
|
40
56
|
@token = authenticate
|
|
41
57
|
|
|
42
|
-
@
|
|
58
|
+
@courier = CDEKApiClient::API::Courier.new(self)
|
|
43
59
|
@location = CDEKApiClient::API::Location.new(self)
|
|
60
|
+
@order = CDEKApiClient::API::Order.new(self)
|
|
61
|
+
@payment = CDEKApiClient::API::Payment.new(self)
|
|
62
|
+
@print = CDEKApiClient::API::Print.new(self)
|
|
44
63
|
@tariff = CDEKApiClient::API::Tariff.new(self)
|
|
45
64
|
@webhook = CDEKApiClient::API::Webhook.new(self)
|
|
46
65
|
end
|
|
@@ -50,16 +69,49 @@ module CDEKApiClient
|
|
|
50
69
|
# @return [String] the access token.
|
|
51
70
|
# @raise [StandardError] if authentication fails.
|
|
52
71
|
def authenticate
|
|
53
|
-
uri = URI(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
client_id: @client_id,
|
|
57
|
-
client_secret: @client_secret
|
|
58
|
-
})
|
|
72
|
+
uri = URI(Config.token_url(custom_url: @base_url))
|
|
73
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
74
|
+
http.use_ssl = true
|
|
59
75
|
|
|
60
|
-
|
|
76
|
+
request = Net::HTTP::Post.new(uri)
|
|
77
|
+
request.set_form_data(
|
|
78
|
+
'grant_type' => 'client_credentials',
|
|
79
|
+
'client_id' => @client_id,
|
|
80
|
+
'client_secret' => @client_secret
|
|
81
|
+
)
|
|
61
82
|
|
|
62
|
-
|
|
83
|
+
response = http.request(request)
|
|
84
|
+
|
|
85
|
+
case response
|
|
86
|
+
when Net::HTTPSuccess
|
|
87
|
+
begin
|
|
88
|
+
data = JSON.parse(response.body)
|
|
89
|
+
auth_response = CDEKApiClient::Entities::AuthResponse.new(
|
|
90
|
+
access_token: data['access_token'],
|
|
91
|
+
token_type: data['token_type'],
|
|
92
|
+
expires_in: data['expires_in'],
|
|
93
|
+
scope: data['scope'],
|
|
94
|
+
jti: data['jti']
|
|
95
|
+
)
|
|
96
|
+
@logger.info("Successfully authenticated, token expires in #{auth_response.expires_in} seconds")
|
|
97
|
+
auth_response.access_token
|
|
98
|
+
rescue JSON::ParserError => e
|
|
99
|
+
raise "Failed to parse authentication response: #{e.message}"
|
|
100
|
+
rescue ArgumentError => e
|
|
101
|
+
raise "Invalid authentication response format: #{e.message}"
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
begin
|
|
105
|
+
error_data = JSON.parse(response.body)
|
|
106
|
+
error_response = CDEKApiClient::Entities::AuthErrorResponse.new(
|
|
107
|
+
error: error_data['error'],
|
|
108
|
+
error_description: error_data['error_description']
|
|
109
|
+
)
|
|
110
|
+
raise "Authentication failed: #{error_response.error} - #{error_response.error_description}"
|
|
111
|
+
rescue JSON::ParserError, ArgumentError
|
|
112
|
+
raise "Authentication failed with HTTP #{response.code}: #{response.body}"
|
|
113
|
+
end
|
|
114
|
+
end
|
|
63
115
|
end
|
|
64
116
|
|
|
65
117
|
# Makes an HTTP request to the API.
|
|
@@ -67,9 +119,11 @@ module CDEKApiClient
|
|
|
67
119
|
# @param method [String] the HTTP method (e.g., 'get', 'post').
|
|
68
120
|
# @param path [String] the API endpoint path.
|
|
69
121
|
# @param body [Hash, nil] the request body.
|
|
122
|
+
# @param query [Hash, nil] the query parameters.
|
|
70
123
|
# @return [Hash, Array] the parsed response.
|
|
71
|
-
def request(method, path, body: nil)
|
|
72
|
-
uri = URI("#{
|
|
124
|
+
def request(method, path, body: nil, query: nil)
|
|
125
|
+
uri = URI("#{@base_url}/#{path}")
|
|
126
|
+
uri.query = URI.encode_www_form(query) if query
|
|
73
127
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
74
128
|
http.use_ssl = true
|
|
75
129
|
request = build_request(method, uri, body)
|
|
@@ -80,6 +134,10 @@ module CDEKApiClient
|
|
|
80
134
|
{ 'error' => e.message }
|
|
81
135
|
end
|
|
82
136
|
|
|
137
|
+
def validate_uuid(uuid)
|
|
138
|
+
raise ArgumentError, 'Invalid UUID format' unless uuid&.match?(/\A[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}\z/i)
|
|
139
|
+
end
|
|
140
|
+
|
|
83
141
|
private
|
|
84
142
|
|
|
85
143
|
# Builds an HTTP request with the specified method, URI, and body.
|
|
@@ -112,8 +170,9 @@ module CDEKApiClient
|
|
|
112
170
|
when Array, Hash
|
|
113
171
|
response
|
|
114
172
|
else
|
|
173
|
+
parsed_response = parse_json(response.body) if response.body
|
|
115
174
|
log_error("Unexpected response type: #{response.class}")
|
|
116
|
-
{ 'error' => "Unexpected response type: #{response.class}" }
|
|
175
|
+
{ 'error' => parsed_response || "Unexpected response type: #{response.class}" }
|
|
117
176
|
end
|
|
118
177
|
rescue JSON::ParserError => e
|
|
119
178
|
log_error("Failed to parse response: #{e.message}")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CDEKApiClient
|
|
4
|
+
# Configuration module for CDEK API Client
|
|
5
|
+
module Config
|
|
6
|
+
# Available API environments
|
|
7
|
+
PRODUCTION = 'production'
|
|
8
|
+
DEMO = 'demo'
|
|
9
|
+
|
|
10
|
+
# API endpoints for different environments
|
|
11
|
+
ENDPOINTS = {
|
|
12
|
+
PRODUCTION => 'https://api.cdek.ru/v2',
|
|
13
|
+
DEMO => 'https://api.edu.cdek.ru/v2'
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
# Get the base URL for the current configuration
|
|
18
|
+
# @param environment [String, Symbol] The environment (:production, :demo, 'production', 'demo')
|
|
19
|
+
# @param custom_url [String] Custom API URL (overrides environment)
|
|
20
|
+
# @return [String] The base API URL
|
|
21
|
+
def base_url(environment: nil, custom_url: nil)
|
|
22
|
+
return custom_url if custom_url
|
|
23
|
+
|
|
24
|
+
env = environment&.to_s&.downcase || ENV.fetch('CDEK_API_ENV', DEMO)
|
|
25
|
+
ENDPOINTS.fetch(env) do
|
|
26
|
+
raise ArgumentError, "Unknown environment: #{env}. Use :production or :demo"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Get the token URL for authentication
|
|
31
|
+
# @param environment [String, Symbol] The environment
|
|
32
|
+
# @param custom_url [String] Custom API URL
|
|
33
|
+
# @return [String] The OAuth token URL
|
|
34
|
+
def token_url(environment: nil, custom_url: nil)
|
|
35
|
+
"#{base_url(environment: environment, custom_url: custom_url)}/oauth/token"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'validatable'
|
|
4
|
+
|
|
5
|
+
module CDEKApiClient
|
|
6
|
+
module Entities
|
|
7
|
+
# Represents an agreement entity for delivery agreements in the CDEK API.
|
|
8
|
+
class Agreement
|
|
9
|
+
include Validatable
|
|
10
|
+
|
|
11
|
+
attr_accessor :cdek_number, :date, :time_from, :time_to, :comment, :delivery_point, :to_location
|
|
12
|
+
|
|
13
|
+
validates :cdek_number, type: :string, presence: true
|
|
14
|
+
validates :date, type: :string, presence: true
|
|
15
|
+
validates :time_from, type: :string, presence: true
|
|
16
|
+
validates :time_to, type: :string, presence: true
|
|
17
|
+
validates :comment, type: :string, presence: false
|
|
18
|
+
validates :delivery_point, type: :string, presence: false
|
|
19
|
+
validates :to_location, type: :hash, presence: false
|
|
20
|
+
|
|
21
|
+
# Initializes a new Agreement object.
|
|
22
|
+
#
|
|
23
|
+
# @param cdek_number [String] the CDEK order number.
|
|
24
|
+
# @param date [String] the delivery date in YYYY-MM-DD format.
|
|
25
|
+
# @param time_from [String] the start time in HH:MM format.
|
|
26
|
+
# @param time_to [String] the end time in HH:MM format.
|
|
27
|
+
# @param comment [String] the comment for the agreement.
|
|
28
|
+
# @param delivery_point [String] the delivery point code.
|
|
29
|
+
# @param to_location [Location] the delivery location.
|
|
30
|
+
# @raise [ArgumentError] if any attribute validation fails.
|
|
31
|
+
def initialize(cdek_number:, date:, time_from:, time_to:, comment: nil, delivery_point: nil, to_location: nil)
|
|
32
|
+
@cdek_number = cdek_number
|
|
33
|
+
@date = date
|
|
34
|
+
@time_from = time_from
|
|
35
|
+
@time_to = time_to
|
|
36
|
+
@comment = comment
|
|
37
|
+
@delivery_point = delivery_point
|
|
38
|
+
@to_location = to_location
|
|
39
|
+
validate!
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Converts the Agreement object to a JSON representation.
|
|
43
|
+
#
|
|
44
|
+
# @return [String] the JSON representation of the Agreement.
|
|
45
|
+
def to_json(*_args)
|
|
46
|
+
data = {
|
|
47
|
+
cdek_number: @cdek_number,
|
|
48
|
+
date: @date,
|
|
49
|
+
time_from: @time_from,
|
|
50
|
+
time_to: @time_to
|
|
51
|
+
}
|
|
52
|
+
data[:comment] = @comment if @comment
|
|
53
|
+
data[:delivery_point] = @delivery_point if @delivery_point
|
|
54
|
+
data[:to_location] = @to_location if @to_location
|
|
55
|
+
data.to_json
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'validatable'
|
|
4
|
+
|
|
5
|
+
module CDEKApiClient
|
|
6
|
+
module Entities
|
|
7
|
+
# Represents an OAuth authentication error response from the CDEK API.
|
|
8
|
+
class AuthErrorResponse
|
|
9
|
+
include Validatable
|
|
10
|
+
|
|
11
|
+
attr_accessor :error, :error_description
|
|
12
|
+
|
|
13
|
+
validates :error, type: :string, presence: true
|
|
14
|
+
validates :error_description, type: :string, presence: true
|
|
15
|
+
|
|
16
|
+
# Initializes a new AuthErrorResponse object.
|
|
17
|
+
#
|
|
18
|
+
# @param error [String] the error code.
|
|
19
|
+
# @param error_description [String] the human-readable error description.
|
|
20
|
+
# @raise [ArgumentError] if any attribute validation fails.
|
|
21
|
+
def initialize(error:, error_description:)
|
|
22
|
+
@error = error
|
|
23
|
+
@error_description = error_description
|
|
24
|
+
validate!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Converts the AuthErrorResponse object to a JSON representation.
|
|
28
|
+
#
|
|
29
|
+
# @return [String] the JSON representation of the AuthErrorResponse.
|
|
30
|
+
def to_json(*_args)
|
|
31
|
+
{
|
|
32
|
+
error: @error,
|
|
33
|
+
error_description: @error_description
|
|
34
|
+
}.to_json
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'validatable'
|
|
4
|
+
|
|
5
|
+
module CDEKApiClient
|
|
6
|
+
module Entities
|
|
7
|
+
# Represents an OAuth authentication response from the CDEK API.
|
|
8
|
+
class AuthResponse
|
|
9
|
+
include Validatable
|
|
10
|
+
|
|
11
|
+
attr_accessor :access_token, :token_type, :expires_in, :scope, :jti
|
|
12
|
+
|
|
13
|
+
validates :access_token, type: :string, presence: true
|
|
14
|
+
validates :token_type, type: :string, presence: true
|
|
15
|
+
validates :expires_in, type: :integer, presence: true
|
|
16
|
+
validates :scope, type: :string, presence: true
|
|
17
|
+
validates :jti, type: :string, presence: true
|
|
18
|
+
|
|
19
|
+
# Initializes a new AuthResponse object.
|
|
20
|
+
#
|
|
21
|
+
# @param access_token [String] the access token for API authentication.
|
|
22
|
+
# @param token_type [String] the type of token (usually "Bearer").
|
|
23
|
+
# @param expires_in [Integer] the number of seconds until the token expires.
|
|
24
|
+
# @param scope [String] the scope of the token.
|
|
25
|
+
# @param jti [String] the unique identifier for the token.
|
|
26
|
+
# @raise [ArgumentError] if any attribute validation fails.
|
|
27
|
+
def initialize(access_token:, token_type:, expires_in:, scope:, jti:)
|
|
28
|
+
@access_token = access_token
|
|
29
|
+
@token_type = token_type
|
|
30
|
+
@expires_in = expires_in
|
|
31
|
+
@scope = scope
|
|
32
|
+
@jti = jti
|
|
33
|
+
validate!
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Converts the AuthResponse object to a JSON representation.
|
|
37
|
+
#
|
|
38
|
+
# @return [String] the JSON representation of the AuthResponse.
|
|
39
|
+
def to_json(*_args)
|
|
40
|
+
{
|
|
41
|
+
access_token: @access_token,
|
|
42
|
+
token_type: @token_type,
|
|
43
|
+
expires_in: @expires_in,
|
|
44
|
+
scope: @scope,
|
|
45
|
+
jti: @jti
|
|
46
|
+
}.to_json
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|