x 0.10.0 → 0.12.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 +47 -28
- data/README.md +12 -2
- data/lib/x/authenticator.rb +10 -0
- data/lib/x/bearer_token_authenticator.rb +5 -3
- data/lib/x/cgi.rb +15 -0
- data/lib/x/client.rb +85 -49
- data/lib/x/connection.rb +39 -76
- data/lib/x/errors/bad_gateway.rb +5 -0
- data/lib/x/errors/{not_found_error.rb → bad_request.rb} +1 -1
- data/lib/x/errors/client_error.rb +2 -2
- data/lib/x/errors/connection_exception.rb +5 -0
- data/lib/x/errors/error.rb +1 -11
- data/lib/x/errors/{forbidden_error.rb → forbidden.rb} +1 -1
- data/lib/x/errors/gateway_timeout.rb +5 -0
- data/lib/x/errors/{bad_request_error.rb → gone.rb} +1 -1
- data/lib/x/errors/http_error.rb +42 -0
- data/lib/x/errors/network_error.rb +1 -1
- data/lib/x/errors/not_acceptable.rb +5 -0
- data/lib/x/errors/not_found.rb +5 -0
- data/lib/x/errors/payload_too_large.rb +5 -0
- data/lib/x/errors/server_error.rb +2 -2
- data/lib/x/errors/service_unavailable.rb +5 -0
- data/lib/x/errors/too_many_redirects.rb +5 -0
- data/lib/x/errors/too_many_requests.rb +24 -0
- data/lib/x/errors/unauthorized.rb +5 -0
- data/lib/x/errors/unprocessable_entity.rb +5 -0
- data/lib/x/{media_upload.rb → media_uploader.rb} +34 -35
- data/lib/x/oauth_authenticator.rb +10 -15
- data/lib/x/redirect_handler.rb +26 -23
- data/lib/x/request_builder.rb +22 -35
- data/lib/x/response_parser.rb +69 -0
- data/lib/x/version.rb +1 -1
- data/sig/x.rbs +118 -92
- metadata +22 -13
- data/lib/x/errors/authentication_error.rb +0 -5
- data/lib/x/errors/payload_too_large_error.rb +0 -5
- data/lib/x/errors/service_unavailable_error.rb +0 -5
- data/lib/x/errors/too_many_redirects_error.rb +0 -5
- data/lib/x/errors/too_many_requests_error.rb +0 -29
- data/lib/x/response_handler.rb +0 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c6252a6765246fe6ca0820e5ce96f9e66d9611e91b2d59753730fad6b87f67e
|
4
|
+
data.tar.gz: 7a7dc55ece60d848cde08c151e887a1cc86808408a864c05934c4be906b2021a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4216ad5a466dd1a714a638a45d9c0996f40c16247f7fc6eb0ff756d301c3cd63aac5ca95fd421940634e3661db5cb41e19300b73f8ef01056c14e21ad89edea1
|
7
|
+
data.tar.gz: a27036e7dd594f9b13b5cd66267c57b63679352ea3eeeaada13adbba838c6cdad5d8f841fe35b808950fc610808c219679e72334091b3d8de1dca5899ba386b5
|
data/CHANGELOG.md
CHANGED
@@ -1,69 +1,88 @@
|
|
1
|
+
## [0.12.0] - 2023-11-02
|
2
|
+
* Ensure Authenticator is passed to RedirectHandler (fc8557b)
|
3
|
+
* Add AUTHENTICATION_HEADER to X::Authenticator base class (85a2818)
|
4
|
+
* Introduce X::HTTPError (90ae132)
|
5
|
+
* Add `code` attribute to error classes (b003639)
|
6
|
+
|
7
|
+
## [0.11.0] - 2023-10-24
|
8
|
+
|
9
|
+
* Add base Authenticator class (8c66ce2)
|
10
|
+
* Consistently use keyword arguments (3beb271)
|
11
|
+
* Use patern matching to build request (4d001c7)
|
12
|
+
* Rename ResponseHandler to ResponseParser (498e890)
|
13
|
+
* Rename methods to be more consistent (5b8c655)
|
14
|
+
* Rename MediaUpload to MediaUploader (84f0c15)
|
15
|
+
* Add mutant and kill mutants (b124968)
|
16
|
+
* Fix authentication bug with request URLs that contain spaces (8de3174)
|
17
|
+
* Refactor errors (853d39c)
|
18
|
+
* Make Connection class threadsafe (d95d285)
|
19
|
+
|
1
20
|
## [0.10.0] - 2023-10-08
|
2
21
|
|
3
|
-
|
4
|
-
|
22
|
+
* Add media upload helper methods (6c6a267)
|
23
|
+
* Add PayloadTooLargeError class (cd61850)
|
5
24
|
|
6
25
|
## [0.9.1] - 2023-10-06
|
7
26
|
|
8
|
-
|
9
|
-
|
10
|
-
|
27
|
+
* Allow successful empty responses (06bf7db)
|
28
|
+
* Update default User-Agent string (296b36a)
|
29
|
+
* Move query parameter escaping into RequestBuilder (56d6bd2)
|
11
30
|
|
12
31
|
## [0.9.0] - 2023-09-26
|
13
32
|
|
14
|
-
|
33
|
+
* Add support for HTTP proxies (3740f4f)
|
15
34
|
|
16
35
|
## [0.8.1] - 2023-09-20
|
17
36
|
|
18
|
-
|
37
|
+
* Fix bug where setting Connection#base_uri= doesn't update the HTTP client (d5a89db)
|
19
38
|
|
20
39
|
## [0.8.0] - 2023-09-14
|
21
40
|
|
22
|
-
|
23
|
-
|
24
|
-
|
41
|
+
* Add (back) bearer token authentication (62e141d)
|
42
|
+
* Follow redirects (90a8c55)
|
43
|
+
* Parse error responses with Content-Type: application/problem+json (0b697d9)
|
25
44
|
|
26
45
|
## [0.7.1] - 2023-09-02
|
27
46
|
|
28
|
-
|
47
|
+
* Fix bug in X::Authenticator#split_uri (ebc9d5f)
|
29
48
|
|
30
49
|
## [0.7.0] - 2023-09-02
|
31
50
|
|
32
|
-
|
51
|
+
* Remove OAuth gem (7c29bb1)
|
33
52
|
|
34
53
|
## [0.6.0] - 2023-08-30
|
35
54
|
|
36
|
-
|
37
|
-
|
38
|
-
|
55
|
+
* Add configurable debug output stream for logging (fd2d4b0)
|
56
|
+
* Remove bearer token authentication (efff940)
|
57
|
+
* Define RBS type signatures (d7f63ba)
|
39
58
|
|
40
59
|
## [0.5.1] - 2023-08-16
|
41
60
|
|
42
|
-
|
61
|
+
* Fix bearer token authentication (1a1ca93)
|
43
62
|
|
44
63
|
## [0.5.0] - 2023-08-10
|
45
64
|
|
46
|
-
|
47
|
-
|
65
|
+
* Add configurable write timeout (2a31f84)
|
66
|
+
* Use built-in Gem::Version class (066e0b6)
|
48
67
|
|
49
68
|
## [0.4.0] - 2023-08-06
|
50
69
|
|
51
|
-
|
52
|
-
|
53
|
-
|
70
|
+
* Refactor Client into Authenticator, RequestBuilder, Connection, ResponseHandler (6bee1e9)
|
71
|
+
* Add configurable open timeout (1000f9d)
|
72
|
+
* Allow configuration of content type (f33a732)
|
54
73
|
|
55
74
|
## [0.3.0] - 2023-08-04
|
56
75
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
76
|
+
* Add accessors to X::Client (e61fa73)
|
77
|
+
* Add configurable read timeout (41502b9)
|
78
|
+
* Handle network-related errors (9ed1fb4)
|
79
|
+
* Include response body in errors (a203e6a)
|
61
80
|
|
62
81
|
## [0.2.0] - 2023-08-02
|
63
82
|
|
64
|
-
|
65
|
-
|
83
|
+
* Allow configuration of base URL (4bc0531)
|
84
|
+
* Improve error handling (14dc0cd)
|
66
85
|
|
67
86
|
## [0.1.0] - 2023-08-02
|
68
87
|
|
69
|
-
|
88
|
+
* Initial release
|
data/README.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+

|
2
|
+

|
3
|
+

|
4
|
+

|
5
|
+
[](https://rubygems.org/gems/x)
|
6
|
+
|
1
7
|
# A [Ruby](https://www.ruby-lang.org) interface to the [X API](https://developer.x.com)
|
2
8
|
|
3
9
|
## Follow
|
@@ -130,11 +136,15 @@ Pull requests will only be accepted if they meet all the following criteria:
|
|
130
136
|
|
131
137
|
bundle exec rake standard
|
132
138
|
|
133
|
-
2.
|
139
|
+
2. 100% C0 code coverage. This can be verified with:
|
134
140
|
|
135
141
|
bundle exec rake test
|
136
142
|
|
137
|
-
3.
|
143
|
+
3. 100% mutation coverage. This can be verified with:
|
144
|
+
|
145
|
+
bundle exec rake mutant
|
146
|
+
|
147
|
+
4. RBS type signatures (in `sig/x.rbs`). This can be verified with:
|
138
148
|
|
139
149
|
bundle exec rake steep
|
140
150
|
|
@@ -1,14 +1,16 @@
|
|
1
|
+
require_relative "authenticator"
|
2
|
+
|
1
3
|
module X
|
2
4
|
# Handles bearer token authentication
|
3
|
-
class BearerTokenAuthenticator
|
5
|
+
class BearerTokenAuthenticator < Authenticator
|
4
6
|
attr_accessor :bearer_token
|
5
7
|
|
6
|
-
def initialize(bearer_token)
|
8
|
+
def initialize(bearer_token:) # rubocop:disable Lint/MissingSuper
|
7
9
|
@bearer_token = bearer_token
|
8
10
|
end
|
9
11
|
|
10
12
|
def header(_request)
|
11
|
-
"Bearer #{bearer_token}"
|
13
|
+
{AUTHENTICATION_HEADER => "Bearer #{bearer_token}"}
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
data/lib/x/cgi.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module X
|
4
|
+
# Namespaced CGI class
|
5
|
+
class CGI
|
6
|
+
# TODO: Replace CGI.escape with CGI.escapeURIComponent when support for Ruby 3.1 is dropped
|
7
|
+
def self.escape(value)
|
8
|
+
::CGI.escape(value).gsub("+", "%20")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.escape_params(params)
|
12
|
+
params.map { |k, v| "#{k}=#{escape(v)}" }.join("&")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/x/client.rb
CHANGED
@@ -1,87 +1,123 @@
|
|
1
1
|
require "forwardable"
|
2
2
|
require_relative "bearer_token_authenticator"
|
3
|
-
require_relative "oauth_authenticator"
|
4
3
|
require_relative "connection"
|
4
|
+
require_relative "oauth_authenticator"
|
5
5
|
require_relative "redirect_handler"
|
6
6
|
require_relative "request_builder"
|
7
|
-
require_relative "
|
7
|
+
require_relative "response_parser"
|
8
8
|
|
9
9
|
module X
|
10
10
|
# Main public interface
|
11
11
|
class Client
|
12
12
|
extend Forwardable
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def_delegators :@
|
20
|
-
def_delegators :@
|
21
|
-
def_delegators :@
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def initialize(
|
27
|
-
|
28
|
-
base_url:
|
14
|
+
DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
|
15
|
+
|
16
|
+
attr_accessor :base_url
|
17
|
+
attr_reader :api_key, :api_key_secret, :access_token, :access_token_secret, :bearer_token
|
18
|
+
|
19
|
+
def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :proxy_url, :debug_output
|
20
|
+
def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :proxy_url=, :debug_output=
|
21
|
+
def_delegators :@redirect_handler, :max_redirects
|
22
|
+
def_delegators :@redirect_handler, :max_redirects=
|
23
|
+
def_delegators :@response_parser, :array_class, :object_class
|
24
|
+
def_delegators :@response_parser, :array_class=, :object_class=
|
25
|
+
|
26
|
+
def initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
|
27
|
+
bearer_token: nil,
|
28
|
+
base_url: DEFAULT_BASE_URL,
|
29
29
|
open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
|
30
30
|
read_timeout: Connection::DEFAULT_READ_TIMEOUT,
|
31
31
|
write_timeout: Connection::DEFAULT_WRITE_TIMEOUT,
|
32
|
+
debug_output: Connection::DEFAULT_DEBUG_OUTPUT,
|
32
33
|
proxy_url: nil,
|
33
|
-
|
34
|
-
|
35
|
-
debug_output: nil,
|
36
|
-
array_class: ResponseHandler::DEFAULT_ARRAY_CLASS,
|
37
|
-
object_class: ResponseHandler::DEFAULT_OBJECT_CLASS,
|
34
|
+
array_class: nil,
|
35
|
+
object_class: nil,
|
38
36
|
max_redirects: RedirectHandler::DEFAULT_MAX_REDIRECTS)
|
39
37
|
|
40
|
-
|
41
|
-
@
|
38
|
+
initialize_oauth(api_key, api_key_secret, access_token, access_token_secret)
|
39
|
+
@bearer_token = bearer_token
|
40
|
+
initialize_authenticator
|
41
|
+
@base_url = base_url
|
42
|
+
@connection = Connection.new(open_timeout: open_timeout, read_timeout: read_timeout,
|
42
43
|
write_timeout: write_timeout, debug_output: debug_output, proxy_url: proxy_url)
|
43
|
-
@request_builder = RequestBuilder.new
|
44
|
-
@redirect_handler = RedirectHandler.new(
|
44
|
+
@request_builder = RequestBuilder.new
|
45
|
+
@redirect_handler = RedirectHandler.new(connection: @connection, request_builder: @request_builder,
|
45
46
|
max_redirects: max_redirects)
|
46
|
-
@
|
47
|
+
@response_parser = ResponseParser.new(array_class: array_class, object_class: object_class)
|
48
|
+
end
|
49
|
+
|
50
|
+
def get(endpoint, headers: {})
|
51
|
+
execute_request(:get, endpoint, headers: headers)
|
52
|
+
end
|
53
|
+
|
54
|
+
def post(endpoint, body = nil, headers: {})
|
55
|
+
execute_request(:post, endpoint, body: body, headers: headers)
|
47
56
|
end
|
48
57
|
|
49
|
-
def
|
50
|
-
|
58
|
+
def put(endpoint, body = nil, headers: {})
|
59
|
+
execute_request(:put, endpoint, body: body, headers: headers)
|
51
60
|
end
|
52
61
|
|
53
|
-
def
|
54
|
-
|
62
|
+
def delete(endpoint, headers: {})
|
63
|
+
execute_request(:delete, endpoint, headers: headers)
|
55
64
|
end
|
56
65
|
|
57
|
-
def
|
58
|
-
|
66
|
+
def api_key=(api_key)
|
67
|
+
@api_key = api_key
|
68
|
+
initialize_authenticator
|
59
69
|
end
|
60
70
|
|
61
|
-
def
|
62
|
-
|
71
|
+
def api_key_secret=(api_key_secret)
|
72
|
+
@api_key_secret = api_key_secret
|
73
|
+
initialize_authenticator
|
74
|
+
end
|
75
|
+
|
76
|
+
def access_token=(access_token)
|
77
|
+
@access_token = access_token
|
78
|
+
initialize_authenticator
|
79
|
+
end
|
80
|
+
|
81
|
+
def access_token_secret=(access_token_secret)
|
82
|
+
@access_token_secret = access_token_secret
|
83
|
+
initialize_authenticator
|
84
|
+
end
|
85
|
+
|
86
|
+
def bearer_token=(bearer_token)
|
87
|
+
@bearer_token = bearer_token
|
88
|
+
initialize_authenticator
|
63
89
|
end
|
64
90
|
|
65
91
|
private
|
66
92
|
|
67
|
-
def
|
68
|
-
@
|
69
|
-
|
70
|
-
|
71
|
-
|
93
|
+
def initialize_oauth(api_key, api_key_secret, access_token, access_token_secret)
|
94
|
+
@api_key = api_key
|
95
|
+
@api_key_secret = api_key_secret
|
96
|
+
@access_token = access_token
|
97
|
+
@access_token_secret = access_token_secret
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize_authenticator
|
101
|
+
@authenticator = if api_key && api_key_secret && access_token && access_token_secret
|
102
|
+
OAuthAuthenticator.new(api_key: api_key, api_key_secret: api_key_secret, access_token: access_token,
|
103
|
+
access_token_secret: access_token_secret)
|
104
|
+
elsif bearer_token
|
105
|
+
BearerTokenAuthenticator.new(bearer_token: bearer_token)
|
106
|
+
elsif @authenticator.nil?
|
107
|
+
Authenticator.new
|
72
108
|
else
|
73
|
-
|
74
|
-
"Client must be initialized with either a bearer_token or " \
|
75
|
-
"an api_key, api_key_secret, access_token, and access_token_secret"
|
109
|
+
@authenticator
|
76
110
|
end
|
77
111
|
end
|
78
112
|
|
79
|
-
def
|
80
|
-
uri = URI.join(
|
81
|
-
request = @request_builder.build(
|
82
|
-
|
83
|
-
|
84
|
-
@
|
113
|
+
def execute_request(http_method, endpoint, headers:, body: nil)
|
114
|
+
uri = URI.join(base_url, endpoint)
|
115
|
+
request = @request_builder.build(http_method: http_method, uri: uri, body: body, headers: headers,
|
116
|
+
authenticator: @authenticator)
|
117
|
+
response = @connection.perform(request: request)
|
118
|
+
response = @redirect_handler.handle(response: response, request: request, base_url: base_url,
|
119
|
+
authenticator: @authenticator)
|
120
|
+
@response_parser.parse(response: response)
|
85
121
|
end
|
86
122
|
end
|
87
123
|
end
|
data/lib/x/connection.rb
CHANGED
@@ -9,12 +9,12 @@ module X
|
|
9
9
|
class Connection
|
10
10
|
extend Forwardable
|
11
11
|
|
12
|
-
|
13
|
-
DEFAULT_HOST = "https://api.twitter.com".freeze
|
12
|
+
DEFAULT_HOST = "api.twitter.com".freeze
|
14
13
|
DEFAULT_PORT = 443
|
15
14
|
DEFAULT_OPEN_TIMEOUT = 60 # seconds
|
16
15
|
DEFAULT_READ_TIMEOUT = 60 # seconds
|
17
16
|
DEFAULT_WRITE_TIMEOUT = 60 # seconds
|
17
|
+
DEFAULT_DEBUG_OUTPUT = File.open(File::NULL, "w")
|
18
18
|
NETWORK_ERRORS = [
|
19
19
|
Errno::ECONNREFUSED,
|
20
20
|
Errno::ECONNRESET,
|
@@ -23,95 +23,58 @@ module X
|
|
23
23
|
OpenSSL::SSL::SSLError
|
24
24
|
].freeze
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def_delegator
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
)
|
26
|
+
attr_accessor :open_timeout, :read_timeout, :write_timeout, :debug_output
|
27
|
+
attr_reader :proxy_url, :proxy_uri
|
28
|
+
|
29
|
+
def_delegator :proxy_uri, :host, :proxy_host
|
30
|
+
def_delegator :proxy_uri, :port, :proxy_port
|
31
|
+
def_delegator :proxy_uri, :user, :proxy_user
|
32
|
+
def_delegator :proxy_uri, :password, :proxy_pass
|
33
|
+
|
34
|
+
def initialize(open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT,
|
35
|
+
write_timeout: DEFAULT_WRITE_TIMEOUT, debug_output: DEFAULT_DEBUG_OUTPUT, proxy_url: nil)
|
36
|
+
@open_timeout = open_timeout
|
37
|
+
@read_timeout = read_timeout
|
38
|
+
@write_timeout = write_timeout
|
39
|
+
@debug_output = debug_output
|
40
|
+
self.proxy_url = proxy_url unless proxy_url.nil?
|
42
41
|
end
|
43
42
|
|
44
|
-
def
|
45
|
-
|
43
|
+
def perform(request:)
|
44
|
+
host = request.uri.host || DEFAULT_HOST
|
45
|
+
port = request.uri.port || DEFAULT_PORT
|
46
|
+
http_client = build_http_client(host, port)
|
47
|
+
http_client.use_ssl = request.uri.scheme.eql?("https")
|
48
|
+
http_client.request(request)
|
46
49
|
rescue *NETWORK_ERRORS => e
|
47
|
-
raise NetworkError
|
50
|
+
raise NetworkError, "Network error: #{e}"
|
48
51
|
end
|
49
52
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@base_uri = base_uri
|
55
|
-
update_http_client_settings
|
56
|
-
end
|
53
|
+
def proxy_url=(proxy_url)
|
54
|
+
@proxy_url = proxy_url
|
55
|
+
proxy_uri = URI(proxy_url)
|
56
|
+
raise ArgumentError, "Invalid proxy URL: #{proxy_uri}" unless proxy_uri.is_a?(URI::HTTP)
|
57
57
|
|
58
|
-
|
59
|
-
@http_client.instance_variable_get(:@debug_output)
|
60
|
-
end
|
61
|
-
|
62
|
-
def configuration
|
63
|
-
{
|
64
|
-
base_url: base_uri.to_s,
|
65
|
-
open_timeout: open_timeout,
|
66
|
-
read_timeout: read_timeout,
|
67
|
-
write_timeout: write_timeout,
|
68
|
-
proxy_url: proxy_uri.to_s,
|
69
|
-
debug_output: debug_output
|
70
|
-
}
|
58
|
+
@proxy_uri = proxy_uri
|
71
59
|
end
|
72
60
|
|
73
61
|
private
|
74
62
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
@http_client.write_timeout = write_timeout
|
79
|
-
@http_client.set_debug_output(debug_output) if debug_output
|
80
|
-
end
|
81
|
-
|
82
|
-
def current_http_client_settings
|
83
|
-
{
|
84
|
-
open_timeout: @http_client.open_timeout,
|
85
|
-
read_timeout: @http_client.read_timeout,
|
86
|
-
write_timeout: @http_client.write_timeout,
|
87
|
-
debug_output: debug_output
|
88
|
-
}
|
89
|
-
end
|
90
|
-
|
91
|
-
def update_http_client_settings
|
92
|
-
conditionally_apply_http_client_settings do
|
93
|
-
host = @base_uri.host || DEFAULT_HOST
|
94
|
-
port = @base_uri.port || DEFAULT_PORT
|
95
|
-
@http_client = build_http_client(host: host, port: port)
|
96
|
-
@http_client.use_ssl = @base_uri.scheme == "https"
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def build_http_client(host:, port:)
|
101
|
-
if defined?(@proxy_uri)
|
102
|
-
Net::HTTP.new(host, port, @proxy_uri&.host, @proxy_uri&.port, @proxy_uri&.user, @proxy_uri&.password)
|
63
|
+
def build_http_client(host = DEFAULT_HOST, port = DEFAULT_PORT)
|
64
|
+
http_client = if proxy_uri
|
65
|
+
Net::HTTP.new(host, port, proxy_host, proxy_port, proxy_user, proxy_pass)
|
103
66
|
else
|
104
67
|
Net::HTTP.new(host, port)
|
105
68
|
end
|
69
|
+
configure_http_client(http_client)
|
106
70
|
end
|
107
71
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
yield
|
72
|
+
def configure_http_client(http_client)
|
73
|
+
http_client.tap do |c|
|
74
|
+
c.open_timeout = open_timeout
|
75
|
+
c.read_timeout = read_timeout
|
76
|
+
c.write_timeout = write_timeout
|
77
|
+
c.set_debug_output(debug_output)
|
115
78
|
end
|
116
79
|
end
|
117
80
|
end
|
data/lib/x/errors/error.rb
CHANGED
@@ -1,13 +1,3 @@
|
|
1
|
-
require "json"
|
2
|
-
|
3
1
|
module X
|
4
|
-
|
5
|
-
class Error < ::StandardError
|
6
|
-
attr_reader :object
|
7
|
-
|
8
|
-
def initialize(msg, response:)
|
9
|
-
@object = JSON.parse(response.body) if response&.body && !response.body.empty?
|
10
|
-
super(msg)
|
11
|
-
end
|
12
|
-
end
|
2
|
+
class Error < StandardError; end
|
13
3
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "json"
|
2
|
+
require_relative "error"
|
3
|
+
|
4
|
+
module X
|
5
|
+
# Base HTTP error class
|
6
|
+
class HTTPError < Error
|
7
|
+
JSON_CONTENT_TYPE_REGEXP = %r{application/(problem\+|)json}
|
8
|
+
|
9
|
+
attr_reader :response, :code
|
10
|
+
|
11
|
+
def initialize(response:)
|
12
|
+
super(error_message(response))
|
13
|
+
@response = response
|
14
|
+
@code = response.code
|
15
|
+
end
|
16
|
+
|
17
|
+
def error_message(response)
|
18
|
+
if json?(response)
|
19
|
+
message_from_json_response(response)
|
20
|
+
else
|
21
|
+
response.message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def message_from_json_response(response)
|
26
|
+
response_object = JSON.parse(response.body)
|
27
|
+
if response_object.key?("title") && response_object.key?("detail")
|
28
|
+
"#{response_object.fetch("title")}: #{response_object.fetch("detail")}"
|
29
|
+
elsif response_object.key?("error")
|
30
|
+
response_object.fetch("error")
|
31
|
+
elsif response_object["errors"].instance_of?(Array)
|
32
|
+
response_object.fetch("errors").map { |error| error.fetch("message") }.join(", ")
|
33
|
+
else
|
34
|
+
response.message
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def json?(response)
|
39
|
+
JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|