dpay-ruby 0.1.1
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 +7 -0
- data/.gitignore +54 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +73 -0
- data/LICENSE +22 -0
- data/README.md +250 -0
- data/Rakefile +332 -0
- data/dpay-ruby.gemspec +37 -0
- data/gource.sh +6 -0
- data/images/Anthony Martin.png +0 -0
- data/lib/dpay.rb +37 -0
- data/lib/dpay/api.rb +229 -0
- data/lib/dpay/base_error.rb +216 -0
- data/lib/dpay/block_api.rb +78 -0
- data/lib/dpay/broadcast.rb +1171 -0
- data/lib/dpay/chain_config.rb +21 -0
- data/lib/dpay/formatter.rb +14 -0
- data/lib/dpay/jsonrpc.rb +108 -0
- data/lib/dpay/mixins/retriable.rb +58 -0
- data/lib/dpay/rpc/base_client.rb +166 -0
- data/lib/dpay/rpc/http_client.rb +127 -0
- data/lib/dpay/rpc/thread_safe_http_client.rb +35 -0
- data/lib/dpay/stream.rb +377 -0
- data/lib/dpay/transaction_builder.rb +386 -0
- data/lib/dpay/type/amount.rb +101 -0
- data/lib/dpay/type/base_type.rb +10 -0
- data/lib/dpay/utils.rb +17 -0
- data/lib/dpay/version.rb +4 -0
- metadata +392 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module DPay
|
|
2
|
+
module ChainConfig
|
|
3
|
+
EXPIRE_IN_SECS = 600
|
|
4
|
+
EXPIRE_IN_SECS_PROPOSAL = 24 * 60 * 60
|
|
5
|
+
|
|
6
|
+
NETWORKS_DPAY_CHAIN_ID = '38f14b346eb697ba04ae0f5adcfaa0a437ed3711197704aa256a14cb9b4a8f26'
|
|
7
|
+
NETWORKS_DPAY_ADDRESS_PREFIX = 'DWB'
|
|
8
|
+
NETWORKS_DPAY_CORE_ASSET = ["0", 3, "@@000000021"] # BEX
|
|
9
|
+
NETWORKS_DPAY_DEBT_ASSET = ["0", 3, "@@000000013"] # BBD
|
|
10
|
+
NETWORKS_DPAY_VEST_ASSET = ["0", 6, "@@000000037"] # VESTS
|
|
11
|
+
NETWORKS_DPAY_DEFAULT_NODE = 'https://api.dpays.io' # √
|
|
12
|
+
NETWORKS_TEST_CHAIN_ID = '46d82ab7d8db682eb1959aed0ada039a6d49afa1602491f93dde9cac3e8e6c32'
|
|
13
|
+
NETWORKS_TEST_ADDRESS_PREFIX = 'DWT'
|
|
14
|
+
NETWORKS_TEST_CORE_ASSET = ["0", 3, "@@000000021"] # TESTS
|
|
15
|
+
NETWORKS_TEST_DEBT_ASSET = ["0", 3, "@@000000013"] # TBD
|
|
16
|
+
NETWORKS_TEST_VEST_ASSET = ["0", 6, "@@000000037"] # VESTS
|
|
17
|
+
NETWORKS_TEST_DEFAULT_NODE = 'https://testnet.dpays.io'
|
|
18
|
+
|
|
19
|
+
NETWORK_CHAIN_IDS = [NETWORKS_DPAY_CHAIN_ID, NETWORKS_TEST_CHAIN_ID]
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/dpay/jsonrpc.rb
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module DPay
|
|
2
|
+
# {Jsonrpc} allows you to inspect the available methods offered by a node.
|
|
3
|
+
# If a node runs a plugin you want, then all of the API methods it can exposes
|
|
4
|
+
# will automatically be available. This API is used internally to determine
|
|
5
|
+
# which APIs and methods are available on the node you specify.
|
|
6
|
+
#
|
|
7
|
+
# In theory, if a new plugin is created and enabled by the node, it will be
|
|
8
|
+
# available by this library without needing an update to its code.
|
|
9
|
+
class Jsonrpc < Api
|
|
10
|
+
API_METHODS = %i(get_signature get_methods)
|
|
11
|
+
|
|
12
|
+
def self.api_methods
|
|
13
|
+
@api_methods ||= {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Might help diagnose a cluster that has asymmetric plugin definitions.
|
|
17
|
+
def self.reset_api_methods
|
|
18
|
+
@api_methods = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(options = {})
|
|
22
|
+
@api_name = self.class.api_name = :jsonrpc
|
|
23
|
+
@methods = API_METHODS
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def get_api_methods(&block)
|
|
28
|
+
api_methods = self.class.api_methods[@rpc_client.uri.to_s]
|
|
29
|
+
|
|
30
|
+
if api_methods.nil?
|
|
31
|
+
get_methods do |result, error, rpc_id|
|
|
32
|
+
raise NotAppBaseError, "#{@rpc_client.uri} does not appear to run AppBase" unless defined? result.map
|
|
33
|
+
|
|
34
|
+
methods = result.map do |method|
|
|
35
|
+
method.split('.').map(&:to_sym)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
api_methods = Hashie::Mash.new
|
|
39
|
+
|
|
40
|
+
methods.each do |api, method|
|
|
41
|
+
api_methods[api] ||= []
|
|
42
|
+
api_methods[api] << method
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
self.class.api_methods[@rpc_client.uri.to_s] = api_methods
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if !!block
|
|
50
|
+
api_methods.each do |api, methods|
|
|
51
|
+
yield api, methods
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
return api_methods
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_all_signatures(&block)
|
|
59
|
+
request_object = []
|
|
60
|
+
method_names = []
|
|
61
|
+
method_map = {}
|
|
62
|
+
signatures = {}
|
|
63
|
+
offset = 0
|
|
64
|
+
|
|
65
|
+
get_api_methods do |api, methods|
|
|
66
|
+
request_object += methods.map do |method|
|
|
67
|
+
method_name = "#{api}.#{method}"
|
|
68
|
+
method_names << method_name
|
|
69
|
+
current_rpc_id = @rpc_client.rpc_id
|
|
70
|
+
offset += 1
|
|
71
|
+
method_map[current_rpc_id] = [api, method]
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
jsonrpc: '2.0',
|
|
75
|
+
id: current_rpc_id,
|
|
76
|
+
method: 'jsonrpc.get_signature',
|
|
77
|
+
params: {method: method_name}
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
chunks = if request_object.size > DPay::RPC::HttpClient::JSON_RPC_BATCH_SIZE_MAXIMUM
|
|
83
|
+
request_object.each_slice(DPay::RPC::HttpClient::JSON_RPC_BATCH_SIZE_MAXIMUM)
|
|
84
|
+
else
|
|
85
|
+
request_object
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
for request_object in chunks do
|
|
89
|
+
@rpc_client.rpc_batch_execute(request_object: request_object) do |result, error, id|
|
|
90
|
+
api, method = method_map[id]
|
|
91
|
+
api = api.to_sym
|
|
92
|
+
method = method.to_sym
|
|
93
|
+
|
|
94
|
+
signatures[api] ||= {}
|
|
95
|
+
signatures[api][method] = result
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if !!block
|
|
99
|
+
signatures.each do |api, methods|
|
|
100
|
+
yield api, methods
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
return signatures unless !!block
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module DPay
|
|
2
|
+
module Retriable
|
|
3
|
+
# @private
|
|
4
|
+
MAX_RETRY_COUNT = 30
|
|
5
|
+
|
|
6
|
+
MAX_RETRY_ELAPSE = 60
|
|
7
|
+
|
|
8
|
+
# @private
|
|
9
|
+
MAX_BACKOFF = MAX_RETRY_ELAPSE / 4
|
|
10
|
+
|
|
11
|
+
RETRYABLE_EXCEPTIONS = [
|
|
12
|
+
NonCanonicalSignatureError, IncorrectRequestIdError,
|
|
13
|
+
IncorrectResponseIdError, RemoteDatabaseLockError
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
def can_retry?(e = nil)
|
|
17
|
+
@retry_count ||= 0
|
|
18
|
+
|
|
19
|
+
return false if @retry_count >= MAX_RETRY_COUNT
|
|
20
|
+
|
|
21
|
+
@retry_count = if retry_reset?
|
|
22
|
+
@first_retry_at = nil
|
|
23
|
+
else
|
|
24
|
+
@retry_count + 1
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
can_retry = case e
|
|
28
|
+
when *RETRYABLE_EXCEPTIONS then true
|
|
29
|
+
else; false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
backoff if can_retry
|
|
33
|
+
|
|
34
|
+
can_retry
|
|
35
|
+
end
|
|
36
|
+
private
|
|
37
|
+
# @private
|
|
38
|
+
def first_retry_at
|
|
39
|
+
@first_retry_at ||= Time.now.utc
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @private
|
|
43
|
+
def retry_reset?
|
|
44
|
+
Time.now.utc - first_retry_at > MAX_RETRY_ELAPSE
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Expontential backoff.
|
|
48
|
+
#
|
|
49
|
+
# @private
|
|
50
|
+
def backoff
|
|
51
|
+
@backoff ||= 0.1
|
|
52
|
+
@backoff *= 2
|
|
53
|
+
@backoff = 0.1 if @backoff > MAX_BACKOFF
|
|
54
|
+
|
|
55
|
+
sleep @backoff
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module DPay
|
|
2
|
+
module RPC
|
|
3
|
+
class BaseClient
|
|
4
|
+
include ChainConfig
|
|
5
|
+
|
|
6
|
+
attr_accessor :url, :chain, :error_pipe
|
|
7
|
+
|
|
8
|
+
# @private
|
|
9
|
+
MAX_TIMEOUT_RETRY_COUNT = 100
|
|
10
|
+
|
|
11
|
+
# @private
|
|
12
|
+
MAX_TIMEOUT_BACKOFF = 30
|
|
13
|
+
|
|
14
|
+
# @private
|
|
15
|
+
TIMEOUT_ERRORS = [Net::ReadTimeout, Errno::EBADF, IOError]
|
|
16
|
+
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
@chain = options[:chain] || :dpay
|
|
19
|
+
@error_pipe = options[:error_pipe] || STDERR
|
|
20
|
+
@api_name = options[:api_name]
|
|
21
|
+
@url = case @chain
|
|
22
|
+
when :dpay then options[:url] || NETWORKS_DPAY_DEFAULT_NODE
|
|
23
|
+
when :test then options[:url] || NETWORKS_TEST_DEFAULT_NODE
|
|
24
|
+
else; raise UnsupportedChainError, "Unsupported chain: #{@chain}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def uri
|
|
29
|
+
@uri ||= URI.parse(url)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Adds a request object to the stack. Usually, this method is called
|
|
33
|
+
# internally by {BaseClient#rpc_execute}. If you want to create a batched
|
|
34
|
+
# request, use this method to add to the batch then execute {BaseClient#rpc_batch_execute}.
|
|
35
|
+
def put(api_name = @api_name, api_method = nil, options = {})
|
|
36
|
+
current_rpc_id = rpc_id
|
|
37
|
+
rpc_method_name = "#{api_name}.#{api_method}"
|
|
38
|
+
options ||= {}
|
|
39
|
+
request_object = defined?(options.delete) ? options.delete(:request_object) : []
|
|
40
|
+
request_object ||= []
|
|
41
|
+
|
|
42
|
+
request_object << {
|
|
43
|
+
jsonrpc: '2.0',
|
|
44
|
+
id: current_rpc_id,
|
|
45
|
+
method: rpc_method_name,
|
|
46
|
+
params: options
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
request_object
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @abstract Subclass is expected to implement #rpc_execute.
|
|
53
|
+
# @!method rpc_execute
|
|
54
|
+
|
|
55
|
+
# @abstract Subclass is expected to implement #rpc_batch_execute.
|
|
56
|
+
# @!method rpc_batch_execute
|
|
57
|
+
|
|
58
|
+
# To be called by {BaseClient#rpc_execute} and {BaseClient#rpc_batch_execute}
|
|
59
|
+
# when a response has been consructed.
|
|
60
|
+
def yield_response(response, &block)
|
|
61
|
+
if !!block
|
|
62
|
+
case response
|
|
63
|
+
when Hashie::Mash then yield response.result, response.error, response.id
|
|
64
|
+
when Hashie::Array
|
|
65
|
+
response.each do |r|
|
|
66
|
+
r = Hashie::Mash.new(r)
|
|
67
|
+
block.call r.result, r.error, r.id
|
|
68
|
+
end
|
|
69
|
+
else; block.call response
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
response
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Checks json-rpc request/response for corrilated id. If they do not
|
|
77
|
+
# match, {IncorrectResponseIdError} is thrown. This is usually caused by
|
|
78
|
+
# the client, involving thread safety. It can also be caused by the node
|
|
79
|
+
# responding without an id.
|
|
80
|
+
#
|
|
81
|
+
# To avoid {IncorrectResponseIdError}, make sure you implement your client
|
|
82
|
+
# correctly.
|
|
83
|
+
#
|
|
84
|
+
# Setting DEBUG=true in the envrionment will cause this method to output
|
|
85
|
+
# both the request and response json.
|
|
86
|
+
#
|
|
87
|
+
# @param options [Hash] options
|
|
88
|
+
# @option options [Boolean] :debug Enable or disable debug output.
|
|
89
|
+
# @option options [Hash] :request to compare id
|
|
90
|
+
# @option options [Hash] :response to compare id
|
|
91
|
+
# @option options [String] :api_method
|
|
92
|
+
# @see {ThreadSafeHttpClient}
|
|
93
|
+
def evaluate_id(options = {})
|
|
94
|
+
debug = options[:debug] || ENV['DEBUG'] == 'true'
|
|
95
|
+
request = options[:request]
|
|
96
|
+
response = options[:response]
|
|
97
|
+
api_method = options[:api_method]
|
|
98
|
+
req_id = request[:id].to_i
|
|
99
|
+
res_id = !!response['id'] ? response['id'].to_i : nil
|
|
100
|
+
method = [@api_name, api_method].join('.')
|
|
101
|
+
|
|
102
|
+
if debug
|
|
103
|
+
req = JSON.pretty_generate(request)
|
|
104
|
+
res = JSON.parse(response) rescue response
|
|
105
|
+
res = JSON.pretty_generate(response) rescue response
|
|
106
|
+
|
|
107
|
+
error_pipe.puts '=' * 80
|
|
108
|
+
error_pipe.puts "Request:"
|
|
109
|
+
error_pipe.puts req
|
|
110
|
+
error_pipe.puts '=' * 80
|
|
111
|
+
error_pipe.puts "Response:"
|
|
112
|
+
error_pipe.puts res
|
|
113
|
+
error_pipe.puts '=' * 80
|
|
114
|
+
error_pipe.puts Thread.current.backtrace.join("\n")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
error = response['error'].to_json if !!response['error']
|
|
118
|
+
|
|
119
|
+
if req_id != res_id
|
|
120
|
+
raise IncorrectResponseIdError, "#{method}: The json-rpc id did not match. Request was: #{req_id}, got: #{res_id.inspect}", BaseError.send(:build_backtrace, error)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Current json-rpc id used for a request. This version auto-increments
|
|
125
|
+
# for each call. Subclasses can use their own strategy.
|
|
126
|
+
def rpc_id
|
|
127
|
+
@rpc_id ||= 0
|
|
128
|
+
@rpc_id += 1
|
|
129
|
+
end
|
|
130
|
+
private
|
|
131
|
+
# @private
|
|
132
|
+
def reset_timeout
|
|
133
|
+
@timeout_retry_count = 0
|
|
134
|
+
@back_off = 0.1
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# @private
|
|
138
|
+
def retry_timeout(context, cause = nil)
|
|
139
|
+
@timeout_retry_count += 1
|
|
140
|
+
|
|
141
|
+
if @timeout_retry_count > MAX_TIMEOUT_RETRY_COUNT
|
|
142
|
+
raise TooManyTimeoutsError.new("Too many timeouts for: #{context}", cause)
|
|
143
|
+
elsif @timeout_retry_count % 10 == 0
|
|
144
|
+
msg = "#{@timeout_retry_count} retry attempts for: #{context}"
|
|
145
|
+
msg += "; cause: #{cause}" if !!cause
|
|
146
|
+
error_pipe.puts msg
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
backoff_timeout
|
|
150
|
+
|
|
151
|
+
context
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Expontential backoff.
|
|
155
|
+
#
|
|
156
|
+
# @private
|
|
157
|
+
def backoff_timeout
|
|
158
|
+
@backoff ||= 0.1
|
|
159
|
+
@backoff *= 2
|
|
160
|
+
@backoff = 0.1 if @backoff > MAX_TIMEOUT_BACKOFF
|
|
161
|
+
|
|
162
|
+
sleep @backoff
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module DPay
|
|
2
|
+
module RPC
|
|
3
|
+
# {HttpClient} is intended for single-threaded applications. For
|
|
4
|
+
# multi-threaded apps, use {ThreadSafeHttpClient}.
|
|
5
|
+
class HttpClient < BaseClient
|
|
6
|
+
# Timeouts are lower level errors, related in that retrying them is
|
|
7
|
+
# trivial, unlike, for example TransactionExpiredError, that *requires*
|
|
8
|
+
# the client to do something before retrying.
|
|
9
|
+
#
|
|
10
|
+
# These situations are hopefully momentary interruptions or rate limiting
|
|
11
|
+
# but they might indicate a bigger problem with the node, so they are not
|
|
12
|
+
# retried forever, only up to MAX_TIMEOUT_RETRY_COUNT and then we give up.
|
|
13
|
+
#
|
|
14
|
+
# *Note:* {JSON::ParserError} is included in this list because under
|
|
15
|
+
# certain timeout conditions, a web server may respond with a generic
|
|
16
|
+
# http status code of 200 and HTML page.
|
|
17
|
+
#
|
|
18
|
+
# @private
|
|
19
|
+
TIMEOUT_ERRORS = [Net::OpenTimeout, JSON::ParserError, Net::ReadTimeout,
|
|
20
|
+
Errno::EBADF, IOError, Errno::ENETDOWN]
|
|
21
|
+
|
|
22
|
+
# @private
|
|
23
|
+
POST_HEADERS = {
|
|
24
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
|
25
|
+
'User-Agent' => DPay::AGENT_ID
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
JSON_RPC_BATCH_SIZE_MAXIMUM = 50
|
|
29
|
+
|
|
30
|
+
def http
|
|
31
|
+
@http ||= Net::HTTP.new(uri.host, uri.port).tap do |http|
|
|
32
|
+
http.use_ssl = true if uri.to_s =~ /^https/i
|
|
33
|
+
http.keep_alive_timeout = 150 # seconds
|
|
34
|
+
|
|
35
|
+
# WARNING This method opens a serious security hole. Never use this
|
|
36
|
+
# method in production code.
|
|
37
|
+
# http.set_debug_output(STDOUT) if !!ENV['DEBUG']
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def http_post
|
|
42
|
+
@http_post ||= Net::HTTP::Post.new(uri.request_uri, POST_HEADERS)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def http_request(request)
|
|
46
|
+
http.request(request)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# This is the main method used by API instances to actually fetch data
|
|
50
|
+
# from the remote node. It abstracts the api namespace, method name, and
|
|
51
|
+
# parameters so that the API instance can be decoupled from the protocol.
|
|
52
|
+
#
|
|
53
|
+
# @param api_name [String] API namespace of the method being called.
|
|
54
|
+
# @param api_method [String] API method name being called.
|
|
55
|
+
# @param options [Hash] options
|
|
56
|
+
# @option options [Object] :request_object Hash or Array to become json in request body.
|
|
57
|
+
def rpc_execute(api_name = @api_name, api_method = nil, options = {}, &block)
|
|
58
|
+
reset_timeout
|
|
59
|
+
|
|
60
|
+
catch :tota_cera_pila do; begin
|
|
61
|
+
request = http_post
|
|
62
|
+
|
|
63
|
+
request_object = if !!api_name && !!api_method
|
|
64
|
+
put(api_name, api_method, options)
|
|
65
|
+
elsif !!options && defined?(options.delete)
|
|
66
|
+
options.delete(:request_object)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
if request_object.size > JSON_RPC_BATCH_SIZE_MAXIMUM
|
|
70
|
+
raise JsonRpcBatchMaximumSizeExceededError, "Maximum json-rpc-batch is #{JSON_RPC_BATCH_SIZE_MAXIMUM} elements."
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
request.body = if request_object.class == Hash
|
|
74
|
+
request_object
|
|
75
|
+
elsif request_object.size == 1
|
|
76
|
+
request_object.first
|
|
77
|
+
else
|
|
78
|
+
request_object
|
|
79
|
+
end.to_json
|
|
80
|
+
|
|
81
|
+
response = catch :http_request do; begin; http_request(request)
|
|
82
|
+
rescue *TIMEOUT_ERRORS => e
|
|
83
|
+
throw retry_timeout(:http_request, e)
|
|
84
|
+
end; end
|
|
85
|
+
|
|
86
|
+
if response.nil?
|
|
87
|
+
throw retry_timeout(:tota_cera_pila, 'response was nil')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
case response.code
|
|
91
|
+
when '200'
|
|
92
|
+
response = catch :parse_json do; begin; JSON[response.body]
|
|
93
|
+
rescue *TIMEOUT_ERRORS => e
|
|
94
|
+
throw retry_timeout(:parse_json, e)
|
|
95
|
+
end; end
|
|
96
|
+
|
|
97
|
+
response = case response
|
|
98
|
+
when Hash
|
|
99
|
+
Hashie::Mash.new(response).tap do |r|
|
|
100
|
+
evaluate_id(request: request_object.first, response: r, api_method: api_method)
|
|
101
|
+
end
|
|
102
|
+
when Array
|
|
103
|
+
Hashie::Array.new(response).tap do |r|
|
|
104
|
+
request_object.each_with_index do |req, index|
|
|
105
|
+
evaluate_id(request: req, response: r[index], api_method: api_method)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
else; response
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
yield_response response, &block
|
|
112
|
+
when '504' # Gateway Timeout
|
|
113
|
+
throw retry_timeout(:tota_cera_pila, response.body)
|
|
114
|
+
when '502' # Bad Gateway
|
|
115
|
+
throw retry_timeout(:tota_cera_pila, response.body)
|
|
116
|
+
else
|
|
117
|
+
raise UnknownError, "#{api_name}.#{api_method}: #{response.body}"
|
|
118
|
+
end
|
|
119
|
+
end; end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def rpc_batch_execute(options = {}, &block)
|
|
123
|
+
yield_response rpc_execute(nil, nil, options), &block
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|