x 0.11.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 +6 -0
- data/README.md +6 -0
- data/lib/x/authenticator.rb +3 -1
- data/lib/x/bearer_token_authenticator.rb +1 -1
- data/lib/x/client.rb +52 -16
- data/lib/x/errors/client_error.rb +2 -2
- data/lib/x/errors/error.rb +1 -6
- data/lib/x/errors/http_error.rb +42 -0
- data/lib/x/errors/server_error.rb +2 -2
- data/lib/x/errors/too_many_redirects.rb +2 -2
- data/lib/x/errors/too_many_requests.rb +4 -9
- data/lib/x/media_uploader.rb +24 -22
- data/lib/x/oauth_authenticator.rb +1 -1
- data/lib/x/redirect_handler.rb +5 -6
- data/lib/x/response_parser.rb +10 -33
- data/lib/x/version.rb +1 -1
- data/sig/x.rbs +35 -23
- metadata +3 -2
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,3 +1,9 @@
|
|
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
|
+
|
1
7
|
## [0.11.0] - 2023-10-24
|
2
8
|
|
3
9
|
* Add base Authenticator class (8c66ce2)
|
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
|
data/lib/x/authenticator.rb
CHANGED
data/lib/x/client.rb
CHANGED
@@ -14,9 +14,8 @@ module X
|
|
14
14
|
DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
|
15
15
|
|
16
16
|
attr_accessor :base_url
|
17
|
+
attr_reader :api_key, :api_key_secret, :access_token, :access_token_secret, :bearer_token
|
17
18
|
|
18
|
-
def_delegators :@authenticator, :bearer_token, :api_key, :api_key_secret, :access_token, :access_token_secret
|
19
|
-
def_delegators :@authenticator, :bearer_token=, :api_key=, :api_key_secret=, :access_token=, :access_token_secret=
|
20
19
|
def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :proxy_url, :debug_output
|
21
20
|
def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :proxy_url=, :debug_output=
|
22
21
|
def_delegators :@redirect_handler, :max_redirects
|
@@ -24,25 +23,27 @@ module X
|
|
24
23
|
def_delegators :@response_parser, :array_class, :object_class
|
25
24
|
def_delegators :@response_parser, :array_class=, :object_class=
|
26
25
|
|
27
|
-
def initialize(
|
28
|
-
|
26
|
+
def initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
|
27
|
+
bearer_token: nil,
|
29
28
|
base_url: DEFAULT_BASE_URL,
|
30
29
|
open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
|
31
30
|
read_timeout: Connection::DEFAULT_READ_TIMEOUT,
|
32
31
|
write_timeout: Connection::DEFAULT_WRITE_TIMEOUT,
|
32
|
+
debug_output: Connection::DEFAULT_DEBUG_OUTPUT,
|
33
33
|
proxy_url: nil,
|
34
|
-
debug_output: nil,
|
35
34
|
array_class: nil,
|
36
35
|
object_class: nil,
|
37
36
|
max_redirects: RedirectHandler::DEFAULT_MAX_REDIRECTS)
|
38
37
|
|
38
|
+
initialize_oauth(api_key, api_key_secret, access_token, access_token_secret)
|
39
|
+
@bearer_token = bearer_token
|
40
|
+
initialize_authenticator
|
39
41
|
@base_url = base_url
|
40
|
-
initialize_authenticator(bearer_token, api_key, api_key_secret, access_token, access_token_secret)
|
41
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
44
|
@request_builder = RequestBuilder.new
|
44
|
-
@redirect_handler = RedirectHandler.new(
|
45
|
-
|
45
|
+
@redirect_handler = RedirectHandler.new(connection: @connection, request_builder: @request_builder,
|
46
|
+
max_redirects: max_redirects)
|
46
47
|
@response_parser = ResponseParser.new(array_class: array_class, object_class: object_class)
|
47
48
|
end
|
48
49
|
|
@@ -62,25 +63,60 @@ module X
|
|
62
63
|
execute_request(:delete, endpoint, headers: headers)
|
63
64
|
end
|
64
65
|
|
66
|
+
def api_key=(api_key)
|
67
|
+
@api_key = api_key
|
68
|
+
initialize_authenticator
|
69
|
+
end
|
70
|
+
|
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
|
89
|
+
end
|
90
|
+
|
65
91
|
private
|
66
92
|
|
67
|
-
def
|
68
|
-
@
|
69
|
-
|
70
|
-
|
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
|
71
102
|
OAuthAuthenticator.new(api_key: api_key, api_key_secret: api_key_secret, access_token: access_token,
|
72
103
|
access_token_secret: access_token_secret)
|
73
|
-
|
104
|
+
elsif bearer_token
|
105
|
+
BearerTokenAuthenticator.new(bearer_token: bearer_token)
|
106
|
+
elsif @authenticator.nil?
|
74
107
|
Authenticator.new
|
108
|
+
else
|
109
|
+
@authenticator
|
75
110
|
end
|
76
111
|
end
|
77
112
|
|
78
113
|
def execute_request(http_method, endpoint, headers:, body: nil)
|
79
114
|
uri = URI.join(base_url, endpoint)
|
80
|
-
request = @request_builder.build(
|
81
|
-
|
115
|
+
request = @request_builder.build(http_method: http_method, uri: uri, body: body, headers: headers,
|
116
|
+
authenticator: @authenticator)
|
82
117
|
response = @connection.perform(request: request)
|
83
|
-
response = @redirect_handler.handle(response: response, request: request, base_url: base_url
|
118
|
+
response = @redirect_handler.handle(response: response, request: request, base_url: base_url,
|
119
|
+
authenticator: @authenticator)
|
84
120
|
@response_parser.parse(response: response)
|
85
121
|
end
|
86
122
|
end
|
data/lib/x/errors/error.rb
CHANGED
@@ -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
|
@@ -1,23 +1,18 @@
|
|
1
1
|
require_relative "client_error"
|
2
2
|
|
3
3
|
module X
|
4
|
-
# Rate limit error
|
4
|
+
# Rate limit error class
|
5
5
|
class TooManyRequests < ClientError
|
6
|
-
def initialize(msg, response)
|
7
|
-
@response = response
|
8
|
-
super(msg)
|
9
|
-
end
|
10
|
-
|
11
6
|
def limit
|
12
|
-
|
7
|
+
response["x-rate-limit-limit"].to_i
|
13
8
|
end
|
14
9
|
|
15
10
|
def remaining
|
16
|
-
|
11
|
+
response["x-rate-limit-remaining"].to_i
|
17
12
|
end
|
18
13
|
|
19
14
|
def reset_at
|
20
|
-
Time.at(
|
15
|
+
Time.at(response["x-rate-limit-reset"].to_i)
|
21
16
|
end
|
22
17
|
|
23
18
|
def reset_in
|
data/lib/x/media_uploader.rb
CHANGED
@@ -19,7 +19,7 @@ module X
|
|
19
19
|
boundary: SecureRandom.hex)
|
20
20
|
validate!(file_path: file_path, media_category: media_category)
|
21
21
|
upload_client = client.dup.tap { |c| c.base_url = "https://upload.twitter.com/1.1/" }
|
22
|
-
upload_body = construct_upload_body(file_path, media_type, boundary)
|
22
|
+
upload_body = construct_upload_body(file_path: file_path, media_type: media_type, boundary: boundary)
|
23
23
|
headers = {"Content-Type" => "multipart/form-data, boundary=#{boundary}"}
|
24
24
|
upload_client.post("media/upload.json?media_category=#{media_category}", upload_body, headers: headers)
|
25
25
|
end
|
@@ -28,10 +28,11 @@ module X
|
|
28
28
|
media_category), boundary: SecureRandom.hex, chunk_size_mb: 8)
|
29
29
|
validate!(file_path: file_path, media_category: media_category)
|
30
30
|
upload_client = client.dup.tap { |c| c.base_url = "https://upload.twitter.com/1.1/" }
|
31
|
-
media = init(upload_client, file_path, media_type,
|
31
|
+
media = init(upload_client: upload_client, file_path: file_path, media_type: media_type,
|
32
|
+
media_category: media_category)
|
32
33
|
chunk_size = chunk_size_mb * BYTES_PER_MB
|
33
|
-
|
34
|
-
|
34
|
+
append(upload_client: upload_client, file_paths: split(file_path, chunk_size), media: media,
|
35
|
+
media_type: media_type, boundary: boundary)
|
35
36
|
upload_client.post("media/upload.json?command=FINALIZE&media_id=#{media["media_id"]}")
|
36
37
|
end
|
37
38
|
|
@@ -64,19 +65,13 @@ module X
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
67
|
-
def init(upload_client, file_path, media_type, media_category)
|
68
|
-
total_bytes = File.size(file_path)
|
69
|
-
query = "command=INIT&media_type=#{media_type}&media_category=#{media_category}&total_bytes=#{total_bytes}"
|
70
|
-
upload_client.post("media/upload.json?#{query}")
|
71
|
-
end
|
72
|
-
|
73
68
|
def split(file_path, chunk_size)
|
74
69
|
file_number = -1
|
75
70
|
|
76
|
-
[].tap do |
|
71
|
+
[].tap do |file_paths|
|
77
72
|
File.open(file_path, "rb") do |f|
|
78
73
|
while (chunk = f.read(chunk_size))
|
79
|
-
|
74
|
+
file_paths << "#{Dir.mktmpdir}/x#{format("%03d", file_number += 1)}".tap do |path|
|
80
75
|
File.write(path, chunk)
|
81
76
|
end
|
82
77
|
end
|
@@ -84,34 +79,41 @@ module X
|
|
84
79
|
end
|
85
80
|
end
|
86
81
|
|
87
|
-
def
|
88
|
-
|
82
|
+
def init(upload_client:, file_path:, media_type:, media_category:)
|
83
|
+
total_bytes = File.size(file_path)
|
84
|
+
query = "command=INIT&media_type=#{media_type}&media_category=#{media_category}&total_bytes=#{total_bytes}"
|
85
|
+
upload_client.post("media/upload.json?#{query}")
|
86
|
+
end
|
87
|
+
|
88
|
+
def append(upload_client:, file_paths:, media:, media_type:, boundary: SecureRandom.hex)
|
89
|
+
threads = file_paths.map.with_index do |file_path, index|
|
89
90
|
Thread.new do
|
90
|
-
upload_body = construct_upload_body(
|
91
|
+
upload_body = construct_upload_body(file_path: file_path, media_type: media_type, boundary: boundary)
|
91
92
|
query = "command=APPEND&media_id=#{media["media_id"]}&segment_index=#{index}"
|
92
93
|
headers = {"Content-Type" => "multipart/form-data, boundary=#{boundary}"}
|
93
|
-
upload_chunk(upload_client, query, upload_body,
|
94
|
+
upload_chunk(upload_client: upload_client, query: query, upload_body: upload_body, file_path: file_path,
|
95
|
+
headers: headers)
|
94
96
|
end
|
95
97
|
end
|
96
98
|
threads.each(&:join)
|
97
99
|
end
|
98
100
|
|
99
|
-
def upload_chunk(upload_client
|
101
|
+
def upload_chunk(upload_client:, query:, upload_body:, file_path:, headers: {})
|
100
102
|
upload_client.post("media/upload.json?#{query}", upload_body, headers: headers)
|
101
103
|
rescue NetworkError, ServerError
|
102
104
|
retries ||= 0
|
103
105
|
((retries += 1) < MAX_RETRIES) ? retry : raise
|
104
106
|
ensure
|
105
|
-
|
107
|
+
cleanup_file(file_path)
|
106
108
|
end
|
107
109
|
|
108
|
-
def
|
109
|
-
dirname = File.dirname(
|
110
|
-
File.delete(
|
110
|
+
def cleanup_file(file_path)
|
111
|
+
dirname = File.dirname(file_path)
|
112
|
+
File.delete(file_path)
|
111
113
|
Dir.delete(dirname) if Dir.empty?(dirname)
|
112
114
|
end
|
113
115
|
|
114
|
-
def construct_upload_body(file_path
|
116
|
+
def construct_upload_body(file_path:, media_type:, boundary: SecureRandom.hex)
|
115
117
|
"--#{boundary}\r\n" \
|
116
118
|
"Content-Disposition: form-data; name=\"media\"; filename=\"#{File.basename(file_path)}\"\r\n" \
|
117
119
|
"Content-Type: #{media_type}\r\n\r\n" \
|
data/lib/x/redirect_handler.rb
CHANGED
@@ -11,23 +11,22 @@ module X
|
|
11
11
|
DEFAULT_MAX_REDIRECTS = 10
|
12
12
|
|
13
13
|
attr_accessor :max_redirects
|
14
|
-
attr_reader :
|
14
|
+
attr_reader :connection, :request_builder
|
15
15
|
|
16
|
-
def initialize(
|
16
|
+
def initialize(connection: Connection.new, request_builder: RequestBuilder.new,
|
17
17
|
max_redirects: DEFAULT_MAX_REDIRECTS)
|
18
|
-
@authenticator = authenticator
|
19
18
|
@connection = connection
|
20
19
|
@request_builder = request_builder
|
21
20
|
@max_redirects = max_redirects
|
22
21
|
end
|
23
22
|
|
24
|
-
def handle(response:, request:, base_url:, redirect_count: 0)
|
23
|
+
def handle(response:, request:, base_url:, authenticator: Authenticator.new, redirect_count: 0)
|
25
24
|
if response.is_a?(Net::HTTPRedirection)
|
26
25
|
raise TooManyRedirects, "Too many redirects" if redirect_count > max_redirects
|
27
26
|
|
28
27
|
new_uri = build_new_uri(response, base_url)
|
29
28
|
|
30
|
-
new_request = build_request(request, new_uri, Integer(response.code))
|
29
|
+
new_request = build_request(request, new_uri, Integer(response.code), authenticator)
|
31
30
|
new_response = connection.perform(request: new_request)
|
32
31
|
|
33
32
|
handle(response: new_response, request: new_request, base_url: base_url, redirect_count: redirect_count + 1)
|
@@ -44,7 +43,7 @@ module X
|
|
44
43
|
URI.join(base_url, location)
|
45
44
|
end
|
46
45
|
|
47
|
-
def build_request(request, new_uri, response_code)
|
46
|
+
def build_request(request, new_uri, response_code, authenticator)
|
48
47
|
http_method, body = case response_code
|
49
48
|
in 307 | 308
|
50
49
|
[request.method.downcase.to_sym, request.body]
|
data/lib/x/response_parser.rb
CHANGED
@@ -3,7 +3,7 @@ require "net/http"
|
|
3
3
|
require_relative "errors/bad_gateway"
|
4
4
|
require_relative "errors/bad_request"
|
5
5
|
require_relative "errors/connection_exception"
|
6
|
-
require_relative "errors/
|
6
|
+
require_relative "errors/http_error"
|
7
7
|
require_relative "errors/forbidden"
|
8
8
|
require_relative "errors/gateway_timeout"
|
9
9
|
require_relative "errors/gone"
|
@@ -19,7 +19,7 @@ require_relative "errors/unprocessable_entity"
|
|
19
19
|
module X
|
20
20
|
# Process HTTP responses
|
21
21
|
class ResponseParser
|
22
|
-
|
22
|
+
ERROR_MAP = {
|
23
23
|
400 => BadRequest,
|
24
24
|
401 => Unauthorized,
|
25
25
|
403 => Forbidden,
|
@@ -35,7 +35,7 @@ module X
|
|
35
35
|
503 => ServiceUnavailable,
|
36
36
|
504 => GatewayTimeout
|
37
37
|
}.freeze
|
38
|
-
JSON_CONTENT_TYPE_REGEXP = %r{application/
|
38
|
+
JSON_CONTENT_TYPE_REGEXP = %r{application/json}
|
39
39
|
|
40
40
|
attr_accessor :array_class, :object_class
|
41
41
|
|
@@ -45,48 +45,25 @@ module X
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def parse(response:)
|
48
|
-
raise error(response) unless
|
48
|
+
raise error(response) unless response.is_a?(Net::HTTPSuccess)
|
49
49
|
|
50
|
-
|
50
|
+
return unless json?(response)
|
51
|
+
|
52
|
+
JSON.parse(response.body, array_class: array_class, object_class: object_class)
|
51
53
|
end
|
52
54
|
|
53
55
|
private
|
54
56
|
|
55
|
-
def success?(response)
|
56
|
-
response.is_a?(Net::HTTPSuccess)
|
57
|
-
end
|
58
|
-
|
59
57
|
def error(response)
|
60
|
-
error_class(response).new(
|
58
|
+
error_class(response).new(response: response)
|
61
59
|
end
|
62
60
|
|
63
61
|
def error_class(response)
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
def error_message(response)
|
68
|
-
if json?(response)
|
69
|
-
message_from_json_response(response)
|
70
|
-
else
|
71
|
-
response.message
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def message_from_json_response(response)
|
76
|
-
response_object = JSON.parse(response.body)
|
77
|
-
if response_object.key?("title") && response_object.key?("detail")
|
78
|
-
"#{response_object.fetch("title")}: #{response_object.fetch("detail")}"
|
79
|
-
elsif response_object.key?("error")
|
80
|
-
response_object.fetch("error")
|
81
|
-
elsif response_object["errors"].instance_of?(Array)
|
82
|
-
response_object.fetch("errors").map { |error| error.fetch("message") }.join(", ")
|
83
|
-
else
|
84
|
-
response.message
|
85
|
-
end
|
62
|
+
ERROR_MAP[Integer(response.code)] || HTTPError
|
86
63
|
end
|
87
64
|
|
88
65
|
def json?(response)
|
89
|
-
|
66
|
+
JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
|
90
67
|
end
|
91
68
|
end
|
92
69
|
end
|
data/lib/x/version.rb
CHANGED
data/sig/x.rbs
CHANGED
@@ -2,16 +2,18 @@ module X
|
|
2
2
|
VERSION: Gem::Version
|
3
3
|
|
4
4
|
class Authenticator
|
5
|
+
AUTHENTICATION_HEADER: String
|
6
|
+
|
5
7
|
def header: (Net::HTTPRequest? request) -> Hash[String, String]
|
6
8
|
end
|
7
9
|
|
8
|
-
class BearerTokenAuthenticator
|
10
|
+
class BearerTokenAuthenticator < Authenticator
|
9
11
|
attr_accessor bearer_token: String
|
10
12
|
def initialize: (bearer_token: String) -> void
|
11
13
|
def header: (Net::HTTPRequest? request) -> Hash[String, String]
|
12
14
|
end
|
13
15
|
|
14
|
-
class OAuthAuthenticator
|
16
|
+
class OAuthAuthenticator < Authenticator
|
15
17
|
OAUTH_VERSION: String
|
16
18
|
OAUTH_SIGNATURE_METHOD: String
|
17
19
|
OAUTH_SIGNATURE_ALGORITHM: String
|
@@ -37,7 +39,10 @@ module X
|
|
37
39
|
def escape: (String value) -> String
|
38
40
|
end
|
39
41
|
|
40
|
-
class
|
42
|
+
class Error < StandardError
|
43
|
+
end
|
44
|
+
|
45
|
+
class ClientError < HTTPError
|
41
46
|
end
|
42
47
|
|
43
48
|
class BadGateway < ClientError
|
@@ -49,8 +54,18 @@ module X
|
|
49
54
|
class ConnectionException < ClientError
|
50
55
|
end
|
51
56
|
|
52
|
-
class
|
53
|
-
|
57
|
+
class HTTPError < Error
|
58
|
+
JSON_CONTENT_TYPE_REGEXP: Regexp
|
59
|
+
|
60
|
+
attr_reader response : Net::HTTPResponse
|
61
|
+
attr_reader code : String
|
62
|
+
|
63
|
+
def initialize: (response: Net::HTTPResponse) -> void
|
64
|
+
|
65
|
+
private
|
66
|
+
def error_message: (Net::HTTPResponse response) -> String
|
67
|
+
def message_from_json_response: (Net::HTTPResponse response) -> String
|
68
|
+
def json?: (Net::HTTPResponse response) -> bool
|
54
69
|
end
|
55
70
|
|
56
71
|
class Forbidden < ClientError
|
@@ -77,19 +92,18 @@ module X
|
|
77
92
|
class PayloadTooLarge < ClientError
|
78
93
|
end
|
79
94
|
|
80
|
-
class ServerError <
|
95
|
+
class ServerError < HTTPError
|
81
96
|
end
|
82
97
|
|
83
98
|
class ServiceUnavailable < ServerError
|
84
99
|
end
|
85
100
|
|
86
|
-
class TooManyRedirects <
|
101
|
+
class TooManyRedirects < Error
|
87
102
|
end
|
88
103
|
|
89
104
|
class TooManyRequests < ClientError
|
90
105
|
@response: Net::HTTPResponse
|
91
106
|
|
92
|
-
def initialize: (String msg, Net::HTTPResponse response) -> void
|
93
107
|
def limit: -> Integer
|
94
108
|
def remaining: -> Integer
|
95
109
|
def reset_at: -> Time
|
@@ -154,19 +168,19 @@ module X
|
|
154
168
|
attr_reader connection: Connection
|
155
169
|
attr_reader request_builder: RequestBuilder
|
156
170
|
attr_reader max_redirects: Integer
|
157
|
-
def initialize: (
|
158
|
-
def handle: (response: Net::HTTPResponse, request: Net::HTTPRequest, base_url: String, ?redirect_count: Integer) -> Net::HTTPResponse
|
171
|
+
def initialize: (connection: Connection, request_builder: RequestBuilder, ?max_redirects: Integer) -> void
|
172
|
+
def handle: (response: Net::HTTPResponse, request: Net::HTTPRequest, base_url: String, ?authenticator: Authenticator, ?redirect_count: Integer) -> Net::HTTPResponse
|
159
173
|
|
160
174
|
private
|
161
175
|
def build_new_uri: (Net::HTTPResponse response, String base_url) -> URI::Generic
|
162
|
-
def build_request: (Net::HTTPRequest request, URI::Generic new_uri, Integer response_code) -> Net::HTTPRequest
|
176
|
+
def build_request: (Net::HTTPRequest request, URI::Generic new_uri, Integer response_code, Authenticator authenticator) -> Net::HTTPRequest
|
163
177
|
def send_new_request: (URI::Generic new_uri, Net::HTTPRequest new_request) -> Net::HTTPResponse
|
164
178
|
end
|
165
179
|
|
166
180
|
class ResponseParser
|
167
181
|
DEFAULT_ARRAY_CLASS: Class
|
168
182
|
DEFAULT_OBJECT_CLASS: Class
|
169
|
-
|
183
|
+
ERROR_MAP: Hash[Integer, singleton(Unauthorized) | singleton(BadRequest) | singleton(Forbidden) | singleton(InternalServerError) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests)]
|
170
184
|
JSON_CONTENT_TYPE_REGEXP: Regexp
|
171
185
|
|
172
186
|
attr_accessor array_class: Class
|
@@ -175,11 +189,8 @@ module X
|
|
175
189
|
def parse: (response: Net::HTTPResponse) -> untyped
|
176
190
|
|
177
191
|
private
|
178
|
-
def success?: (Net::HTTPResponse response) -> bool
|
179
192
|
def error: (Net::HTTPResponse response) -> (Unauthorized | BadRequest | Forbidden | InternalServerError | NotFound | PayloadTooLarge | ServiceUnavailable | TooManyRequests)
|
180
193
|
def error_class: (Net::HTTPResponse response) -> (singleton(Unauthorized) | singleton(BadRequest) | singleton(Forbidden) | singleton(InternalServerError) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests))
|
181
|
-
def error_message: (Net::HTTPResponse response) -> String
|
182
|
-
def message_from_json_response: (Net::HTTPResponse response) -> String
|
183
194
|
def json?: (Net::HTTPResponse response) -> bool
|
184
195
|
end
|
185
196
|
|
@@ -213,14 +224,15 @@ module X
|
|
213
224
|
attr_accessor redirect_handler: RedirectHandler
|
214
225
|
attr_accessor response_parser: ResponseParser
|
215
226
|
|
216
|
-
def initialize: (?
|
227
|
+
def initialize: (?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?bearer_token: String?, ?base_url: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?proxy_url: URI::Generic? | String?, ?debug_output: IO, ?array_class: Class, ?object_class: Class, ?max_redirects: Integer) -> void
|
217
228
|
def get: (String endpoint, ?headers: Hash[String, String]) -> untyped
|
218
229
|
def post: (String endpoint, ?String? body, ?headers: Hash[String, String]) -> untyped
|
219
230
|
def put: (String endpoint, ?String? body, ?headers: Hash[String, String]) -> untyped
|
220
231
|
def delete: (String endpoint, ?headers: Hash[String, String]) -> untyped
|
221
232
|
|
222
233
|
private
|
223
|
-
def
|
234
|
+
def initialize_oauth: (String? api_key, String? api_key_secret, String? access_token, String? access_token_secret) -> void
|
235
|
+
def initialize_authenticator: -> Authenticator
|
224
236
|
def execute_request: (Symbol http_method, String endpoint, headers: Hash[String, String], ?body: String?) -> untyped
|
225
237
|
end
|
226
238
|
|
@@ -253,13 +265,13 @@ module X
|
|
253
265
|
private
|
254
266
|
def validate!: (file_path: String, media_category: String) -> nil
|
255
267
|
def infer_media_type: (String file_path, String media_category) -> String
|
256
|
-
def init: (Client upload_client, String file_path, String media_type, String media_category) -> untyped
|
257
268
|
def split: (String file_path, Integer chunk_size) -> Array[String]
|
258
|
-
def
|
259
|
-
def
|
260
|
-
def
|
261
|
-
def
|
262
|
-
def
|
269
|
+
def init: (upload_client: Client, file_path: String, media_type: String, media_category: String) -> untyped
|
270
|
+
def append: (upload_client: Client, file_paths: Array[String], media: untyped, media_type: String, ?boundary: String) -> Array[String]
|
271
|
+
def upload_chunk: (upload_client: Client, query: String, upload_body: String, file_path: String, ?headers: Hash[String, String]) -> Integer?
|
272
|
+
def cleanup_file: (String file_path) -> Integer?
|
273
|
+
def finalize: (upload_client: Client, media: untyped) -> untyped
|
274
|
+
def construct_upload_body: (file_path: String, media_type: String, ?boundary: String) -> String
|
263
275
|
end
|
264
276
|
|
265
277
|
class CGI
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: x
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erik Berlin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- lib/x/errors/forbidden.rb
|
37
37
|
- lib/x/errors/gateway_timeout.rb
|
38
38
|
- lib/x/errors/gone.rb
|
39
|
+
- lib/x/errors/http_error.rb
|
39
40
|
- lib/x/errors/internal_server_error.rb
|
40
41
|
- lib/x/errors/network_error.rb
|
41
42
|
- lib/x/errors/not_acceptable.rb
|