rest-client-wrapper 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +484 -0
- data/lib/rest_client_wrapper.rb +33 -0
- data/lib/rest_client_wrapper/authenticators/auth.rb +30 -0
- data/lib/rest_client_wrapper/authenticators/basic.rb +45 -0
- data/lib/rest_client_wrapper/authenticators/custom.rb +61 -0
- data/lib/rest_client_wrapper/authenticators/oauth.rb +90 -0
- data/lib/rest_client_wrapper/authenticators/token.rb +43 -0
- data/lib/rest_client_wrapper/constants.rb +28 -0
- data/lib/rest_client_wrapper/exceptions.rb +37 -0
- data/lib/rest_client_wrapper/http.rb +66 -0
- data/lib/rest_client_wrapper/paginators/echo.rb +67 -0
- data/lib/rest_client_wrapper/paginators/echo_paginator.rb +67 -0
- data/lib/rest_client_wrapper/paginators/header_link.rb +78 -0
- data/lib/rest_client_wrapper/paginators/header_link_paginator.rb +78 -0
- data/lib/rest_client_wrapper/paginators/paginate.rb +32 -0
- data/lib/rest_client_wrapper/request.rb +75 -0
- data/lib/rest_client_wrapper/response.rb +34 -0
- data/lib/rest_client_wrapper/rest_client.rb +178 -0
- data/lib/rest_client_wrapper/version.rb +23 -0
- data/rest-client-wrapper.gemspec +51 -0
- data/spec/authenticators/oauth_spec.rb +103 -0
- data/spec/factories/request.rb +48 -0
- data/spec/factories/response.rb +47 -0
- data/spec/request_spec.rb +185 -0
- data/spec/rest_client_spec.rb +249 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/support/factory_bot.rb +28 -0
- data/spec/support/stub_helper.rb +20 -0
- metadata +216 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require_relative "paginate"
|
20
|
+
require_relative "../exceptions"
|
21
|
+
|
22
|
+
module RestClientWrapper
|
23
|
+
|
24
|
+
# Paginator
|
25
|
+
module Paginator
|
26
|
+
|
27
|
+
include Paginate
|
28
|
+
|
29
|
+
# Echo
|
30
|
+
class Echo
|
31
|
+
|
32
|
+
attr_accessor :rest_client
|
33
|
+
|
34
|
+
def initialize(limit: Paginate::DEFAULT_PAGINATION_PAGE_SIZE)
|
35
|
+
@rest_client = nil
|
36
|
+
@config = { limit: limit }
|
37
|
+
end
|
38
|
+
|
39
|
+
def paginate(http_method:, uri:, query_params: {}, headers: {}, data: false)
|
40
|
+
raise RestClientError.new("Client not set, unable to make API call", nil, nil) unless @rest_client
|
41
|
+
|
42
|
+
query_params.reverse_merge!(@config)
|
43
|
+
responses = []
|
44
|
+
loop do
|
45
|
+
response = @rest_client.make_request({ http_method: http_method, uri: uri, query_params: query_params, headers: headers })
|
46
|
+
block_given? ? yield(response) : (responses << response)
|
47
|
+
links = _pagination_links(response)
|
48
|
+
break unless links.key?(:offset)
|
49
|
+
|
50
|
+
query_params[:offset] = links[:offset]
|
51
|
+
end
|
52
|
+
return data ? responses.map(&:body).pluck(:data).flatten : responses
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def _pagination_links(response)
|
58
|
+
next_l = response&.body&.[](:next) || ""
|
59
|
+
next_h = Rack::Utils.parse_query(URI.parse(next_l)&.query)
|
60
|
+
return next_h.symbolize_keys!
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require_relative "paginate"
|
20
|
+
require_relative "../exceptions"
|
21
|
+
|
22
|
+
module RestClientWrapper
|
23
|
+
|
24
|
+
# Paginator
|
25
|
+
module Paginator
|
26
|
+
|
27
|
+
include Paginate
|
28
|
+
|
29
|
+
# HeaderLink
|
30
|
+
class HeaderLink
|
31
|
+
|
32
|
+
attr_accessor :rest_client
|
33
|
+
|
34
|
+
def initialize(per_page: Paginate::DEFAULT_PAGINATION_PAGE_SIZE)
|
35
|
+
@rest_client = nil
|
36
|
+
@config = { page: nil, per_page: per_page }
|
37
|
+
end
|
38
|
+
|
39
|
+
def paginate(http_method:, uri:, query_params: {}, headers: {}, data: false)
|
40
|
+
raise RestClientError.new("Client not set, unable to make API call", nil, nil) unless @rest_client
|
41
|
+
|
42
|
+
query_params.reverse_merge!(@config)
|
43
|
+
responses = []
|
44
|
+
loop.with_index(1) do |_, page|
|
45
|
+
query_params[:page] = page
|
46
|
+
response = @rest_client.make_request({ http_method: http_method, uri: uri, query_params: query_params, headers: headers })
|
47
|
+
block_given? ? yield(response) : (responses << response)
|
48
|
+
links = _pagination_links(response)
|
49
|
+
break unless links.key?(:next)
|
50
|
+
end
|
51
|
+
return data ? responses.map(&:body).flatten : responses
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def _pagination_links(response)
|
57
|
+
re_uri = "\<(.*?)\>".freeze
|
58
|
+
re_rel = "current|next|first|last".freeze
|
59
|
+
links_a = response&.headers&.[](:link)&.split(",") || []
|
60
|
+
links_h = {}
|
61
|
+
links_a.each do |rel_link|
|
62
|
+
link_parts = rel_link.split(";")
|
63
|
+
next unless link_parts.length == 2
|
64
|
+
|
65
|
+
uri_match = link_parts[0].match(re_uri)
|
66
|
+
rel_match = link_parts[1].match(re_rel)
|
67
|
+
next if (uri_match.nil? || rel_match.nil?) || (uri_match.captures.length != 1 || rel_match.length != 1)
|
68
|
+
|
69
|
+
links_h[rel_match[0]] = uri_match.captures[0]
|
70
|
+
end
|
71
|
+
return links_h.symbolize_keys!
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require_relative "paginate"
|
20
|
+
require_relative "../exceptions"
|
21
|
+
|
22
|
+
module RestClientWrapper
|
23
|
+
|
24
|
+
# Paginator
|
25
|
+
module Paginator
|
26
|
+
|
27
|
+
include Paginate
|
28
|
+
|
29
|
+
# HeaderLink
|
30
|
+
class HeaderLink
|
31
|
+
|
32
|
+
attr_accessor :rest_client
|
33
|
+
|
34
|
+
def initialize(per_page: Paginate::DEFAULT_PAGINATION_PAGE_SIZE)
|
35
|
+
@rest_client = nil
|
36
|
+
@config = { page: nil, per_page: per_page }
|
37
|
+
end
|
38
|
+
|
39
|
+
def paginate(http_method:, uri:, query_params: {}, headers: {}, data: false)
|
40
|
+
raise RestClientError.new("Client not set, unable to make API call", nil, nil) unless @rest_client
|
41
|
+
|
42
|
+
query_params.reverse_merge!(@config)
|
43
|
+
responses = []
|
44
|
+
loop.with_index(1) do |_, page|
|
45
|
+
query_params[:page] = page
|
46
|
+
response = @rest_client.make_request({ http_method: http_method, uri: uri, query_params: query_params, headers: headers })
|
47
|
+
block_given? ? yield(response) : (responses << response)
|
48
|
+
links = _pagination_links(response)
|
49
|
+
break unless links.key?(:next)
|
50
|
+
end
|
51
|
+
return data ? responses.map(&:body).flatten : responses
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def _pagination_links(response)
|
57
|
+
re_uri = "\<(.*?)\>".freeze
|
58
|
+
re_rel = "current|next|first|last".freeze
|
59
|
+
links_a = response&.headers&.[](:link)&.split(",") || []
|
60
|
+
links_h = {}
|
61
|
+
links_a.each do |rel_link|
|
62
|
+
link_parts = rel_link.split(";")
|
63
|
+
next unless link_parts.length == 2
|
64
|
+
|
65
|
+
uri_match = link_parts[0].match(re_uri)
|
66
|
+
rel_match = link_parts[1].match(re_rel)
|
67
|
+
next if (uri_match.nil? || rel_match.nil?) || (uri_match.captures.length != 1 || rel_match.length != 1)
|
68
|
+
|
69
|
+
links_h[rel_match[0]] = uri_match.captures[0]
|
70
|
+
end
|
71
|
+
return links_h.symbolize_keys!
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
module RestClientWrapper
|
20
|
+
|
21
|
+
# Interface for paginators to implement
|
22
|
+
module Paginate
|
23
|
+
|
24
|
+
DEFAULT_PAGINATION_PAGE_SIZE = 1000
|
25
|
+
|
26
|
+
def paginate(_http_method:, _uri:, _payload:, _headers:, _data:)
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
module RestClientWrapper
|
20
|
+
|
21
|
+
# Request
|
22
|
+
class Request
|
23
|
+
|
24
|
+
attr_reader :uri, :headers, :http_method, :payload, :segment_params, :query_params
|
25
|
+
attr_writer :uri
|
26
|
+
|
27
|
+
DEFAULT_CONTENT_TYPE = { content_type: :json, accept: :json }.freeze # default content type for post and put requests
|
28
|
+
VALID_HTTP_METHODS = %i[get post put patch delete connect options trace].freeze
|
29
|
+
HTTP_METHOD_FOR_JSON = %i[post put patch].freeze
|
30
|
+
|
31
|
+
def initialize(http_method:, uri:, segment_params: {}, payload: {}, query_params: {}, headers: {}) # rubocop:disable Metrics/ParameterLists
|
32
|
+
@uri = uri
|
33
|
+
self.headers = headers
|
34
|
+
self.segment_params = segment_params
|
35
|
+
self.payload = payload
|
36
|
+
self.query_params = query_params
|
37
|
+
self.http_method = http_method
|
38
|
+
end
|
39
|
+
|
40
|
+
def http_method=(http_method)
|
41
|
+
raise TypeError, "Request http_method parameters is not a symbol" unless http_method.is_a?(Symbol)
|
42
|
+
raise ArgumentError, "Not a valid http method" unless VALID_HTTP_METHODS.include?(http_method)
|
43
|
+
|
44
|
+
headers[:content_type] = DEFAULT_CONTENT_TYPE[:content_type] unless headers.key?(:content_type) || !HTTP_METHOD_FOR_JSON.include?(http_method)
|
45
|
+
headers[:accept] = DEFAULT_CONTENT_TYPE[:accept] unless headers.key?(:accept) || !HTTP_METHOD_FOR_JSON.include?(http_method)
|
46
|
+
@http_method = http_method
|
47
|
+
end
|
48
|
+
|
49
|
+
def payload=(payload)
|
50
|
+
raise TypeError, "Request payload parameters is not a hash" unless payload.is_a?(Hash)
|
51
|
+
|
52
|
+
@payload = payload
|
53
|
+
end
|
54
|
+
|
55
|
+
def segment_params=(segment_params)
|
56
|
+
raise TypeError, "Request segment parameters is not a hash" unless segment_params.is_a?(Hash)
|
57
|
+
|
58
|
+
@segment_params = segment_params
|
59
|
+
end
|
60
|
+
|
61
|
+
def query_params=(query_params)
|
62
|
+
raise TypeError, "Request query parameters is not a hash" unless query_params.is_a?(Hash)
|
63
|
+
|
64
|
+
@query_params = query_params
|
65
|
+
end
|
66
|
+
|
67
|
+
def headers=(headers)
|
68
|
+
raise TypeError, "Request headers parameters is not a hash" unless headers.is_a?(Hash)
|
69
|
+
|
70
|
+
@headers.nil? ? @headers = headers : @headers.merge!(headers)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
module RestClientWrapper
|
20
|
+
|
21
|
+
# Response
|
22
|
+
class Response
|
23
|
+
|
24
|
+
attr_reader :headers, :body, :code
|
25
|
+
|
26
|
+
def initialize(headers, body, code)
|
27
|
+
@headers = headers
|
28
|
+
@body = body
|
29
|
+
@code = code
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# Copyright (C) 2019 The University of Adelaide
|
2
|
+
#
|
3
|
+
# This file is part of Rest-Client-Wrapper.
|
4
|
+
#
|
5
|
+
# Rest-Client-Wrapper is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# Rest-Client-Wrapper is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with Rest-Client-Wrapper. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
#
|
18
|
+
|
19
|
+
require "active_support"
|
20
|
+
require "active_support/core_ext"
|
21
|
+
require "json"
|
22
|
+
require "rack"
|
23
|
+
require "rest_client"
|
24
|
+
require_relative "exceptions"
|
25
|
+
require_relative "http"
|
26
|
+
|
27
|
+
module RestClientWrapper
|
28
|
+
|
29
|
+
# RestClient
|
30
|
+
class RestClient
|
31
|
+
|
32
|
+
include Http
|
33
|
+
|
34
|
+
attr_accessor :authenticator, :paginator
|
35
|
+
|
36
|
+
DEFAULT_RETRY = { max_retry: 0, wait: 0 }.freeze
|
37
|
+
DEFAULT_CONFIG = {
|
38
|
+
retries: {
|
39
|
+
401 => { max_retry: 1, wait: 0 }, # unauthorized
|
40
|
+
429 => { max_retry: 3, wait: 3 } # too many requests
|
41
|
+
}
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
def initialize(host:, config: {})
|
45
|
+
@host = host
|
46
|
+
@config = config
|
47
|
+
@retry_configs = {}.reverse_merge(DEFAULT_CONFIG[:retries])
|
48
|
+
|
49
|
+
@config[:retries]&.each do |k, v|
|
50
|
+
next unless Rack::Utils::HTTP_STATUS_CODES[k.to_i] # skip invalid codes
|
51
|
+
|
52
|
+
@retry_configs[k.to_i] = v.reverse_merge(DEFAULT_RETRY)
|
53
|
+
end
|
54
|
+
|
55
|
+
_reset_retries
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute(request:)
|
59
|
+
_reset_retries
|
60
|
+
_validate_request(request)
|
61
|
+
_set_auth(request)
|
62
|
+
url = _build_uri(request)
|
63
|
+
|
64
|
+
loop do
|
65
|
+
access_token = @authenticator.is_a?(Authenticator::Oauth) ? @authenticator.access_token : nil
|
66
|
+
response_code = nil
|
67
|
+
|
68
|
+
begin
|
69
|
+
payload = Request::HTTP_METHOD_FOR_JSON.include?(request.http_method) ? request.payload.to_json : request.payload
|
70
|
+
request.headers[:params] = request.query_params
|
71
|
+
response = ::RestClient::Request.execute({ method: request.http_method, url: url, payload: payload, headers: request.headers })
|
72
|
+
response_code = response&.code
|
73
|
+
rescue StandardError => e
|
74
|
+
response_code = e.response&.code
|
75
|
+
# Any response that doesn't have a status of 200...207 will be raised as an exception from Rest-Client
|
76
|
+
raise RestClientError.new("API request encountered an unhandled exception", e.response, e) unless _retry?(response_code)
|
77
|
+
end
|
78
|
+
|
79
|
+
return Response.new(response.headers, _parse_response(response), response.code) if Http.success?(response_code)
|
80
|
+
raise RestClientNotSuccessful.new("API request was not successful", response) unless _retry?(response_code)
|
81
|
+
|
82
|
+
_wait_and_retry(response_code, access_token)
|
83
|
+
next
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def make_request(http_method:, uri:, payload: {}, query_params: {}, headers: {})
|
88
|
+
parsed_uri = URI.parse(uri)
|
89
|
+
|
90
|
+
raise ArgumentError, "URL host does not match config host of instance, unable to make API call" if parsed_uri.absolute? && @host.casecmp("#{ parsed_uri.scheme }://#{ parsed_uri.host }").nonzero?
|
91
|
+
|
92
|
+
uri = parsed_uri.absolute? ? parsed_uri.path : uri
|
93
|
+
request = Request.new({ http_method: http_method, uri: uri, payload: payload, query_params: query_params })
|
94
|
+
request.headers = headers
|
95
|
+
return self.execute({ request: request })
|
96
|
+
end
|
97
|
+
|
98
|
+
def make_request_for_pages(http_method:, uri:, query_params: {}, headers: {}, data: false)
|
99
|
+
raise RestClientError.new("Paginator not set, unable to make API call", nil, nil) unless @paginator
|
100
|
+
|
101
|
+
@paginator.rest_client ||= self
|
102
|
+
return @paginator.paginate({ http_method: http_method, uri: uri, query_params: query_params, headers: headers, data: data })
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def _set_auth(request)
|
108
|
+
return if @authenticator.nil?
|
109
|
+
|
110
|
+
auth = @authenticator.respond_to?(:generate_auth) ? @authenticator.generate_auth : {}
|
111
|
+
if @authenticator.is_a?(Authenticator::Custom)
|
112
|
+
case @authenticator.type
|
113
|
+
when :query_param
|
114
|
+
request.query_params.merge!(auth)
|
115
|
+
when :header
|
116
|
+
request.headers.merge!(auth)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
request.headers.merge!(auth)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def _build_uri(request)
|
124
|
+
return format("#{ @host }#{ request.uri }", request.segment_params)
|
125
|
+
end
|
126
|
+
|
127
|
+
def _validate_request(request)
|
128
|
+
# Regex to find segments in uri with the pattern <segment_param>
|
129
|
+
url_segments = request.uri.scan(/\<(.*?)\>/).flatten
|
130
|
+
url_segments.each do |url_segment|
|
131
|
+
raise ArgumentError, "Segment parameter not provided for #{ url_segment }. URI #{ request.uri }" unless request.segment_params.include? url_segment.to_sym
|
132
|
+
end
|
133
|
+
return true
|
134
|
+
end
|
135
|
+
|
136
|
+
def _parse_response(response)
|
137
|
+
return nil unless response.respond_to?(:body)
|
138
|
+
|
139
|
+
parsed_body =
|
140
|
+
case MIME::Types[response&.headers&.[](:content_type)].first
|
141
|
+
when "application/json"
|
142
|
+
_parse_json(response)
|
143
|
+
else
|
144
|
+
response.body
|
145
|
+
end
|
146
|
+
|
147
|
+
return parsed_body
|
148
|
+
rescue StandardError => e
|
149
|
+
raise RestClientError.new("Response could not be parsed", response, e)
|
150
|
+
end
|
151
|
+
|
152
|
+
def _parse_json(response)
|
153
|
+
return { ok: true } if response.body == "ok".to_json # Handle special case for Echo delete responses
|
154
|
+
|
155
|
+
return JSON.parse(response.body, { object_class: Hash, symbolize_names: true })
|
156
|
+
rescue StandardError => e
|
157
|
+
raise RestClientError.new("Response could not be parsed as JSON", response, e)
|
158
|
+
end
|
159
|
+
|
160
|
+
def _reset_retries
|
161
|
+
@retry_configs.each_value { |v| v[:retry] = 0 }
|
162
|
+
end
|
163
|
+
|
164
|
+
def _retry?(response_code)
|
165
|
+
return true if @retry_configs.key?(response_code) && @retry_configs[response_code][:retry] < @retry_configs[response_code][:max_retry]
|
166
|
+
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
|
170
|
+
def _wait_and_retry(response_code, access_token)
|
171
|
+
sleep(@retry_configs[response_code][:wait].to_f)
|
172
|
+
Authenticator::Oauth.authenticate({ client_id: @authenticator&.client_id, access_token: access_token }) if Http.unauthorized?(response_code) && @authenticator.is_a?(Authenticator::Oauth)
|
173
|
+
@retry_configs[response_code][:retry] += 1
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|