x 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5af820e6e3cd8b04becf201ad7af96c8d95d9206112673888f91c5ecf923187
4
- data.tar.gz: 6f0f561be192153eca2cc7356c823c87bd98f0987256875892c0a2e9ea12b82d
3
+ metadata.gz: 5c6252a6765246fe6ca0820e5ce96f9e66d9611e91b2d59753730fad6b87f67e
4
+ data.tar.gz: 7a7dc55ece60d848cde08c151e887a1cc86808408a864c05934c4be906b2021a
5
5
  SHA512:
6
- metadata.gz: 8b208d5ba10604a34a57b0dd2ee5dc0992eb801cfbcc287a7f1b0d54444f214050a2c25b2ec6291868a8c35fff09260ad91a82641be830b87d3e6abf0207fc3e
7
- data.tar.gz: 24c6a1083358b60065698320372d53404bab9c319aaad13b097508a7041653ac9d45aabd8e6f7a99204accddfc1beeeb2bce18bd67fa9ba816eb6282cbab4227
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
+ ![Tests](https://github.com/sferik/x-ruby/actions/workflows/test.yml/badge.svg)
2
+ ![Linter](https://github.com/sferik/x-ruby/actions/workflows/lint.yml/badge.svg)
3
+ ![Mutant](https://github.com/sferik/x-ruby/actions/workflows/mutant.yml/badge.svg)
4
+ ![Typer Checker](https://github.com/sferik/x-ruby/actions/workflows/type_check.yml/badge.svg)
5
+ [![Gem Version](https://badge.fury.io/rb/x.svg)](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
@@ -1,8 +1,10 @@
1
1
  module X
2
2
  # Base Authenticator class
3
3
  class Authenticator
4
+ AUTHENTICATION_HEADER = "Authorization".freeze
5
+
4
6
  def header(_request)
5
- {"Authorization" => ""}
7
+ {AUTHENTICATION_HEADER => ""}
6
8
  end
7
9
  end
8
10
  end
@@ -10,7 +10,7 @@ module X
10
10
  end
11
11
 
12
12
  def header(_request)
13
- {"Authorization" => "Bearer #{bearer_token}"}
13
+ {AUTHENTICATION_HEADER => "Bearer #{bearer_token}"}
14
14
  end
15
15
  end
16
16
  end
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(bearer_token: nil,
28
- api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
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(authenticator: @authenticator, connection: @connection,
45
- request_builder: @request_builder, max_redirects: max_redirects)
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 initialize_authenticator(bearer_token, api_key, api_key_secret, access_token, access_token_secret)
68
- @authenticator = if bearer_token
69
- BearerTokenAuthenticator.new(bearer_token: bearer_token)
70
- elsif api_key && api_key_secret && access_token && access_token_secret
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
- else
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(authenticator: @authenticator, http_method: http_method, uri: uri, body: body,
81
- headers: headers)
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
@@ -1,5 +1,5 @@
1
- require_relative "error"
1
+ require_relative "http_error"
2
2
 
3
3
  module X
4
- class ClientError < Error; end
4
+ class ClientError < HTTPError; end
5
5
  end
@@ -1,8 +1,3 @@
1
1
  module X
2
- # Base error class
3
- class Error < ::StandardError
4
- def initialize(msg, _response = nil)
5
- super(msg)
6
- end
7
- end
2
+ class Error < StandardError; end
8
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
@@ -1,5 +1,5 @@
1
- require_relative "error"
1
+ require_relative "http_error"
2
2
 
3
3
  module X
4
- class ServerError < Error; end
4
+ class ServerError < HTTPError; end
5
5
  end
@@ -1,5 +1,5 @@
1
- require_relative "client_error"
1
+ require_relative "error"
2
2
 
3
3
  module X
4
- class TooManyRedirects < ClientError; end
4
+ class TooManyRedirects < Error; end
5
5
  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
- @response["x-rate-limit-limit"].to_i
7
+ response["x-rate-limit-limit"].to_i
13
8
  end
14
9
 
15
10
  def remaining
16
- @response["x-rate-limit-remaining"].to_i
11
+ response["x-rate-limit-remaining"].to_i
17
12
  end
18
13
 
19
14
  def reset_at
20
- Time.at(@response["x-rate-limit-reset"].to_i)
15
+ Time.at(response["x-rate-limit-reset"].to_i)
21
16
  end
22
17
 
23
18
  def reset_in
@@ -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, media_category)
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
- chunk_paths = split(file_path, chunk_size)
34
- append(upload_client, chunk_paths, media, media_type, boundary)
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 |chunk_paths|
71
+ [].tap do |file_paths|
77
72
  File.open(file_path, "rb") do |f|
78
73
  while (chunk = f.read(chunk_size))
79
- chunk_paths << "#{Dir.mktmpdir}/x#{format("%03d", file_number += 1)}".tap do |path|
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 append(upload_client, chunk_paths, media, media_type, boundary = SecureRandom.hex)
88
- threads = chunk_paths.map.with_index do |chunk_path, index|
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(chunk_path, media_type, boundary)
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, chunk_path, headers)
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, query, upload_body, chunk_path, headers = {})
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
- cleanup_chunk(chunk_path)
107
+ cleanup_file(file_path)
106
108
  end
107
109
 
108
- def cleanup_chunk(chunk_path)
109
- dirname = File.dirname(chunk_path)
110
- File.delete(chunk_path)
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, media_type, boundary = SecureRandom.hex)
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" \
@@ -24,7 +24,7 @@ module X
24
24
 
25
25
  def header(request)
26
26
  method, url, query_params = parse_request(request)
27
- {"Authorization" => build_oauth_header(method, url, query_params)}
27
+ {AUTHENTICATION_HEADER => build_oauth_header(method, url, query_params)}
28
28
  end
29
29
 
30
30
  private
@@ -11,23 +11,22 @@ module X
11
11
  DEFAULT_MAX_REDIRECTS = 10
12
12
 
13
13
  attr_accessor :max_redirects
14
- attr_reader :authenticator, :connection, :request_builder
14
+ attr_reader :connection, :request_builder
15
15
 
16
- def initialize(authenticator: Authenticator.new, connection: Connection.new, request_builder: RequestBuilder.new,
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]
@@ -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/error"
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
- ERROR_CLASSES = {
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/(problem\+|)json}
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 success?(response)
48
+ raise error(response) unless response.is_a?(Net::HTTPSuccess)
49
49
 
50
- JSON.parse(response.body, array_class: array_class, object_class: object_class) if json?(response)
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(error_message(response), response)
58
+ error_class(response).new(response: response)
61
59
  end
62
60
 
63
61
  def error_class(response)
64
- ERROR_CLASSES[Integer(response.code)] || Error
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
- response.body && JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
66
+ JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
90
67
  end
91
68
  end
92
69
  end
data/lib/x/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "rubygems/version"
2
2
 
3
3
  module X
4
- VERSION = Gem::Version.create("0.11.0")
4
+ VERSION = Gem::Version.create("0.12.0")
5
5
  end
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 ClientError < Error
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 Error < StandardError
53
- def initialize: (String msg, ?Net::HTTPResponse? response) -> void
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 < Error
95
+ class ServerError < HTTPError
81
96
  end
82
97
 
83
98
  class ServiceUnavailable < ServerError
84
99
  end
85
100
 
86
- class TooManyRedirects < ClientError
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: (authenticator: Authenticator, connection: Connection, request_builder: RequestBuilder, ?max_redirects: Integer) -> void
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
- ERROR_CLASSES: Hash[Integer, singleton(Unauthorized) | singleton(BadRequest) | singleton(Forbidden) | singleton(InternalServerError) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests)]
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: (?bearer_token: String?, ?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: 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
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 initialize_authenticator: (String? bearer_token, String? api_key, String? api_key_secret, String? access_token, String? access_token_secret) -> (BearerTokenAuthenticator | OAuthAuthenticator | Authenticator)
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 append: (Client upload_client, Array[String] chunk_paths, untyped media, String media_type, ?String boundary) -> Array[String]
259
- def upload_chunk: (Client upload_client, String query, String chunk_path, String media_type, ?Hash[String, String] headers) -> Integer?
260
- def cleanup_chunk: (String chunk_path) -> Integer?
261
- def finalize: (Client upload_client, untyped media) -> untyped
262
- def construct_upload_body: (String file_path, String media_type, ?String boundary) -> String
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.11.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-10-24 00:00:00.000000000 Z
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