smartsheet 1.0.0.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/LICENSE +202 -0
- data/README.md +139 -0
- data/Rakefile +13 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/smartsheet.rb +3 -0
- data/lib/smartsheet/api/body_builder.rb +26 -0
- data/lib/smartsheet/api/endpoint_spec.rb +35 -0
- data/lib/smartsheet/api/faraday_adapter/faraday_net_client.rb +42 -0
- data/lib/smartsheet/api/faraday_adapter/faraday_response.rb +60 -0
- data/lib/smartsheet/api/faraday_adapter/middleware/faraday_error_translator.rb +20 -0
- data/lib/smartsheet/api/faraday_adapter/middleware/response_parser.rb +25 -0
- data/lib/smartsheet/api/file_spec.rb +28 -0
- data/lib/smartsheet/api/header_builder.rb +85 -0
- data/lib/smartsheet/api/request.rb +29 -0
- data/lib/smartsheet/api/request_client.rb +26 -0
- data/lib/smartsheet/api/request_logger.rb +148 -0
- data/lib/smartsheet/api/request_spec.rb +43 -0
- data/lib/smartsheet/api/response_net_client_decorator.rb +47 -0
- data/lib/smartsheet/api/retry_logic.rb +37 -0
- data/lib/smartsheet/api/retry_net_client_decorator.rb +36 -0
- data/lib/smartsheet/api/url_builder.rb +25 -0
- data/lib/smartsheet/client.rb +115 -0
- data/lib/smartsheet/constants.rb +16 -0
- data/lib/smartsheet/endpoints/contacts/contacts.rb +29 -0
- data/lib/smartsheet/endpoints/favorites/favorites.rb +158 -0
- data/lib/smartsheet/endpoints/folders/folders.rb +124 -0
- data/lib/smartsheet/endpoints/groups/groups.rb +82 -0
- data/lib/smartsheet/endpoints/home/home.rb +19 -0
- data/lib/smartsheet/endpoints/reports/reports.rb +96 -0
- data/lib/smartsheet/endpoints/reports/reports_share.rb +68 -0
- data/lib/smartsheet/endpoints/search/search.rb +29 -0
- data/lib/smartsheet/endpoints/server_info/server_info.rb +19 -0
- data/lib/smartsheet/endpoints/share/share.rb +58 -0
- data/lib/smartsheet/endpoints/sheets/cells.rb +80 -0
- data/lib/smartsheet/endpoints/sheets/columns.rb +65 -0
- data/lib/smartsheet/endpoints/sheets/comments.rb +60 -0
- data/lib/smartsheet/endpoints/sheets/comments_attachments.rb +77 -0
- data/lib/smartsheet/endpoints/sheets/discussions.rb +80 -0
- data/lib/smartsheet/endpoints/sheets/discussions_attachments.rb +21 -0
- data/lib/smartsheet/endpoints/sheets/rows.rb +91 -0
- data/lib/smartsheet/endpoints/sheets/rows_attachments.rb +91 -0
- data/lib/smartsheet/endpoints/sheets/sheets.rb +301 -0
- data/lib/smartsheet/endpoints/sheets/sheets_attachments.rb +173 -0
- data/lib/smartsheet/endpoints/sheets/sheets_share.rb +68 -0
- data/lib/smartsheet/endpoints/sights/sights.rb +97 -0
- data/lib/smartsheet/endpoints/sights/sights_share.rb +68 -0
- data/lib/smartsheet/endpoints/templates/templates.rb +28 -0
- data/lib/smartsheet/endpoints/token/token.rb +57 -0
- data/lib/smartsheet/endpoints/update_requests/sent_update_requests.rb +42 -0
- data/lib/smartsheet/endpoints/update_requests/update_requests.rb +69 -0
- data/lib/smartsheet/endpoints/users/alternate_emails.rb +77 -0
- data/lib/smartsheet/endpoints/users/users.rb +73 -0
- data/lib/smartsheet/endpoints/webhooks/webhooks.rb +70 -0
- data/lib/smartsheet/endpoints/workspaces/workspaces.rb +83 -0
- data/lib/smartsheet/endpoints/workspaces/workspaces_share.rb +68 -0
- data/lib/smartsheet/error.rb +30 -0
- data/lib/smartsheet/general_request.rb +53 -0
- data/lib/smartsheet/version.rb +5 -0
- data/read-write-sheet/config.json +4 -0
- data/read-write-sheet/read_write_sheet.rb +89 -0
- data/smartsheet.gemspec +47 -0
- metadata +279 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'url_builder'
|
2
|
+
|
3
|
+
module Smartsheet
|
4
|
+
module API
|
5
|
+
class EndpointSpec
|
6
|
+
attr_reader :method, :url_segments, :spec
|
7
|
+
|
8
|
+
def initialize(method, url, **spec)
|
9
|
+
@method = method
|
10
|
+
@url_segments = url
|
11
|
+
@spec = spec
|
12
|
+
end
|
13
|
+
|
14
|
+
def requires_auth?
|
15
|
+
!spec.key?(:no_auth)
|
16
|
+
end
|
17
|
+
|
18
|
+
def requires_body?
|
19
|
+
spec.key? :body_type
|
20
|
+
end
|
21
|
+
|
22
|
+
def sending_file?
|
23
|
+
requires_body? && spec[:body_type] == :file
|
24
|
+
end
|
25
|
+
|
26
|
+
def sending_json?
|
27
|
+
requires_body? && spec[:body_type] == :json
|
28
|
+
end
|
29
|
+
|
30
|
+
def headers
|
31
|
+
spec.key?(:headers) ? spec[:headers] : {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'smartsheet/api/request'
|
3
|
+
require 'smartsheet/api/faraday_adapter/middleware/faraday_error_translator'
|
4
|
+
require 'smartsheet/api/faraday_adapter/middleware/response_parser'
|
5
|
+
|
6
|
+
module Smartsheet
|
7
|
+
module API
|
8
|
+
class FaradayNetClient
|
9
|
+
def initialize
|
10
|
+
create_connection
|
11
|
+
end
|
12
|
+
|
13
|
+
# Expected output:
|
14
|
+
# - returned Success Response
|
15
|
+
# - returned Error Response
|
16
|
+
# - thrown Request Error
|
17
|
+
def make_request(request)
|
18
|
+
response = conn.send(request.method) do |req|
|
19
|
+
req.url(request.url)
|
20
|
+
req.headers = request.headers
|
21
|
+
req.params = request.params
|
22
|
+
req.body = request.body
|
23
|
+
end
|
24
|
+
|
25
|
+
response.body
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def create_connection
|
31
|
+
@conn = Faraday.new do |conn|
|
32
|
+
conn.use Middleware::FaradayErrorTranslator
|
33
|
+
conn.use Middleware::ResponseParser
|
34
|
+
|
35
|
+
conn.adapter Faraday.default_adapter
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :conn
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Smartsheet
|
2
|
+
module API
|
3
|
+
class FaradayResponse
|
4
|
+
def self.from_response_env(faraday_response)
|
5
|
+
if faraday_response[:body].kind_of?(Hash) && faraday_response[:body].key?(:errorCode)
|
6
|
+
FaradayErrorResponse.new(faraday_response[:body], faraday_response)
|
7
|
+
else
|
8
|
+
FaradaySuccessResponse.new(faraday_response[:body], faraday_response)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :status_code, :reason_phrase, :headers
|
13
|
+
|
14
|
+
def initialize(faraday_response)
|
15
|
+
@status_code = faraday_response[:status]
|
16
|
+
@reason_phrase = faraday_response[:reason_phrase]
|
17
|
+
@headers = faraday_response[:response_headers]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class FaradayErrorResponse < FaradayResponse
|
22
|
+
RETRYABLE_ERRORS = 4001..4004
|
23
|
+
|
24
|
+
attr_reader :error_code, :message, :ref_id, :detail
|
25
|
+
|
26
|
+
def initialize(result, faraday_response)
|
27
|
+
super(faraday_response)
|
28
|
+
@error_code = result[:errorCode]
|
29
|
+
@message = result[:message]
|
30
|
+
@ref_id = result[:refId]
|
31
|
+
@detail = result[:detail]
|
32
|
+
end
|
33
|
+
|
34
|
+
def should_retry?
|
35
|
+
RETRYABLE_ERRORS.include? error_code
|
36
|
+
end
|
37
|
+
|
38
|
+
def success?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class FaradaySuccessResponse < FaradayResponse
|
44
|
+
attr_reader :result
|
45
|
+
|
46
|
+
def initialize(result, faraday_response)
|
47
|
+
super(faraday_response)
|
48
|
+
@result = result
|
49
|
+
end
|
50
|
+
|
51
|
+
def should_retry?
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def success?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'smartsheet/error'
|
3
|
+
|
4
|
+
module Smartsheet
|
5
|
+
module API
|
6
|
+
module Middleware
|
7
|
+
class FaradayErrorTranslator < Faraday::Middleware
|
8
|
+
def initialize(app)
|
9
|
+
super(app)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
@app.call(env)
|
14
|
+
rescue Faraday::Error => e
|
15
|
+
raise Smartsheet::RequestError, e
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
require 'smartsheet/api/faraday_adapter/faraday_response'
|
4
|
+
|
5
|
+
module Smartsheet
|
6
|
+
module API
|
7
|
+
module Middleware
|
8
|
+
class ResponseParser < Faraday::Middleware
|
9
|
+
def initialize(app)
|
10
|
+
super(app)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
@app.call(env).on_complete do |response_env|
|
15
|
+
if response_env[:response_headers]['content-type'] =~ /\bjson\b/
|
16
|
+
response_env[:body] = JSON.parse(response_env[:body], symbolize_names: true)
|
17
|
+
end
|
18
|
+
|
19
|
+
response_env[:body] = FaradayResponse.from_response_env response_env
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
module Smartsheet
|
5
|
+
module API
|
6
|
+
class PathFileSpec
|
7
|
+
attr_reader :upload_io, :filename, :content_type, :file_length
|
8
|
+
|
9
|
+
def initialize(path, filename, content_type)
|
10
|
+
@file_length = File.size(path)
|
11
|
+
@filename = filename.nil? ? File.basename(path) : filename
|
12
|
+
@upload_io = Faraday::UploadIO.new(path, content_type, CGI::escape(@filename))
|
13
|
+
@content_type = content_type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ObjectFileSpec
|
18
|
+
attr_reader :upload_io, :filename, :content_type, :file_length
|
19
|
+
|
20
|
+
def initialize(file, filename, file_length, content_type)
|
21
|
+
@file_length = file_length
|
22
|
+
@filename = filename
|
23
|
+
@upload_io = Faraday::UploadIO.new(file, content_type, CGI::escape(filename))
|
24
|
+
@content_type = content_type
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'smartsheet/version'
|
3
|
+
require 'smartsheet/constants'
|
4
|
+
|
5
|
+
module Smartsheet
|
6
|
+
module API
|
7
|
+
# Constructs headers for accessing the Smartsheet API
|
8
|
+
class HeaderBuilder
|
9
|
+
include Smartsheet::Constants
|
10
|
+
def initialize(token, endpoint_spec, request_spec, assume_user: nil)
|
11
|
+
@token = token
|
12
|
+
@endpoint_spec = endpoint_spec
|
13
|
+
@request_spec = request_spec
|
14
|
+
@assume_user = assume_user
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
base_headers
|
19
|
+
.merge(endpoint_headers)
|
20
|
+
.merge(content_type)
|
21
|
+
.merge(content_disposition)
|
22
|
+
.merge(content_length)
|
23
|
+
.merge(request_headers)
|
24
|
+
.merge(assume_user)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_accessor :endpoint_spec, :request_spec
|
30
|
+
attr_reader :token
|
31
|
+
|
32
|
+
def base_headers
|
33
|
+
base = {
|
34
|
+
Accept: JSON_TYPE,
|
35
|
+
'User-Agent': "#{USER_AGENT}/#{Smartsheet::VERSION}"
|
36
|
+
}
|
37
|
+
base[:Authorization] = "Bearer #{token}" if endpoint_spec.requires_auth?
|
38
|
+
|
39
|
+
base
|
40
|
+
end
|
41
|
+
|
42
|
+
def assume_user
|
43
|
+
if @assume_user.nil?
|
44
|
+
{}
|
45
|
+
else
|
46
|
+
{'Assume-User': CGI::escape(@assume_user)}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def endpoint_headers
|
51
|
+
endpoint_spec.headers
|
52
|
+
end
|
53
|
+
|
54
|
+
def content_type
|
55
|
+
if endpoint_spec.sending_json? && request_spec.body
|
56
|
+
{'Content-Type': JSON_TYPE}
|
57
|
+
elsif endpoint_spec.sending_file?
|
58
|
+
{'Content-Type': request_spec.content_type}
|
59
|
+
else
|
60
|
+
{}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def content_disposition
|
65
|
+
if endpoint_spec.sending_file?
|
66
|
+
{'Content-Disposition': "attachment; filename=\"#{CGI::escape(request_spec.filename)}\""}
|
67
|
+
else
|
68
|
+
{}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def content_length
|
73
|
+
if endpoint_spec.sending_file?
|
74
|
+
{'Content-Length': request_spec.file_length.to_s}
|
75
|
+
else
|
76
|
+
{}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def request_headers
|
81
|
+
request_spec.header_overrides
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'smartsheet/api/url_builder'
|
2
|
+
require 'smartsheet/api/header_builder'
|
3
|
+
require 'smartsheet/api/body_builder'
|
4
|
+
|
5
|
+
module Smartsheet
|
6
|
+
module API
|
7
|
+
class Request
|
8
|
+
attr_reader :method, :url, :headers, :params, :body
|
9
|
+
|
10
|
+
def initialize(token, endpoint_spec, request_spec, base_url, assume_user: nil)
|
11
|
+
@method = endpoint_spec.method
|
12
|
+
@url = Smartsheet::API::UrlBuilder.new(endpoint_spec, request_spec, base_url).build
|
13
|
+
@headers = Smartsheet::API::HeaderBuilder.new(token, endpoint_spec, request_spec, assume_user: assume_user).build
|
14
|
+
@params = request_spec.params
|
15
|
+
@body = Smartsheet::API::BodyBuilder.new(endpoint_spec, request_spec).build
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.class == self.class && other.equality_state == equality_state
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def equality_state
|
25
|
+
[method, url, headers, params, body]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'smartsheet/error'
|
2
|
+
|
3
|
+
module Smartsheet
|
4
|
+
module API
|
5
|
+
class RequestClient
|
6
|
+
def initialize(token, client, base_url, assume_user: nil, logger: MuteRequestLogger.new)
|
7
|
+
@token = token
|
8
|
+
@client = client
|
9
|
+
@assume_user = assume_user
|
10
|
+
@logger = logger
|
11
|
+
@base_url = base_url
|
12
|
+
end
|
13
|
+
|
14
|
+
def make_request(endpoint_spec, request_spec)
|
15
|
+
request = Request.new(token, endpoint_spec, request_spec, base_url, assume_user: assume_user)
|
16
|
+
|
17
|
+
logger.log_request(request)
|
18
|
+
client.make_request(request)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :token, :client, :assume_user, :logger, :base_url
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Smartsheet
|
4
|
+
module API
|
5
|
+
class Censor
|
6
|
+
EXPOSED_CHARS = 4
|
7
|
+
|
8
|
+
def initialize(*blacklist)
|
9
|
+
@blacklist = Set.new(blacklist)
|
10
|
+
end
|
11
|
+
|
12
|
+
def censor_hash(h)
|
13
|
+
h.collect do |(k, v)|
|
14
|
+
new_v = blacklist.include?(k.to_s) ? censor(v) : v
|
15
|
+
[k, new_v]
|
16
|
+
end.to_h
|
17
|
+
end
|
18
|
+
|
19
|
+
def censor(str)
|
20
|
+
total_length = str.length
|
21
|
+
censored_length = [total_length - EXPOSED_CHARS, 0].max
|
22
|
+
('*' * censored_length) + str[censored_length...total_length]
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :blacklist
|
28
|
+
end
|
29
|
+
|
30
|
+
class RequestLogger
|
31
|
+
QUERY_PARAM_CENSOR = Censor.new 'code', 'client_id', 'hash', 'refresh_token'
|
32
|
+
HEADER_CENSOR = Censor.new 'authorization'
|
33
|
+
PAYLOAD_CENSOR = Censor.new 'access_token', 'refresh_token'
|
34
|
+
|
35
|
+
TRUNCATED_BODY_LENGTH = 1024
|
36
|
+
|
37
|
+
def initialize(logger, log_full_body:)
|
38
|
+
@logger = logger
|
39
|
+
@log_full_body = log_full_body
|
40
|
+
end
|
41
|
+
|
42
|
+
def log_request(request)
|
43
|
+
log_request_basics(Logger::INFO, request)
|
44
|
+
log_headers('Request', request)
|
45
|
+
log_body('Request', request.body)
|
46
|
+
end
|
47
|
+
|
48
|
+
def log_retry_attempt(request, response, attempt_num)
|
49
|
+
logger.warn { "Request attempt #{attempt_num} failed" }
|
50
|
+
log_request_basics(Logger::WARN, request)
|
51
|
+
log_api_error(Logger::WARN, response)
|
52
|
+
end
|
53
|
+
|
54
|
+
def log_retry_failure(num_tries)
|
55
|
+
try_word = num_tries == 1 ? 'try' : 'tries'
|
56
|
+
logger.error { "Request failed after #{num_tries} #{try_word}" }
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_successful_response(response)
|
60
|
+
log_status(Logger::INFO, response)
|
61
|
+
log_headers('Response', response)
|
62
|
+
log_body('Response', response.result)
|
63
|
+
end
|
64
|
+
|
65
|
+
def log_error_response(request, error)
|
66
|
+
log_request_basics(Logger::ERROR, request)
|
67
|
+
log_api_error(Logger::ERROR, error)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
attr_reader :logger, :log_full_body
|
73
|
+
|
74
|
+
def log_request_basics(level, request)
|
75
|
+
logger.log(level) { "Request: #{request.method} #{build_logging_url(request)}" }
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_logging_url(request)
|
79
|
+
query_params = QUERY_PARAM_CENSOR.censor_hash(request.params)
|
80
|
+
query_param_str =
|
81
|
+
if query_params.empty?
|
82
|
+
''
|
83
|
+
else
|
84
|
+
'?' + query_params.collect { |(k, v)| "#{k}=#{v}" }.join('&') # TODO: URI Encoding
|
85
|
+
end
|
86
|
+
request.url + query_param_str
|
87
|
+
end
|
88
|
+
|
89
|
+
def log_api_error(level, response)
|
90
|
+
log_status(level, response)
|
91
|
+
logger.log(level) do
|
92
|
+
"#{response.error_code}: #{response.message} - Ref ID: #{response.ref_id}"
|
93
|
+
end
|
94
|
+
log_headers('Response', response)
|
95
|
+
end
|
96
|
+
|
97
|
+
def log_status(level, response)
|
98
|
+
logger.log(level) { "Response: #{response.status_code} #{response.reason_phrase}" }
|
99
|
+
end
|
100
|
+
|
101
|
+
def log_headers(context, req_or_resp)
|
102
|
+
logger.debug { "#{context} Headers: #{HEADER_CENSOR.censor_hash(req_or_resp.headers)}" }
|
103
|
+
end
|
104
|
+
|
105
|
+
def log_body(context, body)
|
106
|
+
return unless body
|
107
|
+
|
108
|
+
body_str =
|
109
|
+
if body.is_a? String
|
110
|
+
body
|
111
|
+
elsif body.is_a? Hash
|
112
|
+
PAYLOAD_CENSOR.censor_hash(body).to_s
|
113
|
+
else
|
114
|
+
'<Binary body>'
|
115
|
+
end
|
116
|
+
|
117
|
+
body_str = truncate_body(body_str) unless log_full_body
|
118
|
+
|
119
|
+
logger.debug "#{context} Body: #{body_str}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def truncate_body(body_str)
|
123
|
+
if body_str.length > TRUNCATED_BODY_LENGTH
|
124
|
+
body_str[0...TRUNCATED_BODY_LENGTH] + '...'
|
125
|
+
else
|
126
|
+
body_str
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class MuteRequestLogger
|
132
|
+
def log_request(request)
|
133
|
+
end
|
134
|
+
|
135
|
+
def log_retry_attempt(request, response, attempt_num)
|
136
|
+
end
|
137
|
+
|
138
|
+
def log_retry_failure(num_retries)
|
139
|
+
end
|
140
|
+
|
141
|
+
def log_successful_response(response)
|
142
|
+
end
|
143
|
+
|
144
|
+
def log_error_response(request, error)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|