x 0.9.1 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e2906018cf3e05342abe1bc6c938f3d1b3cf9d1d84c3813a96a11b693fbe0960
4
- data.tar.gz: f84f47e1a9ea06b101712ae1f9fd7198d71b2cbc7487a654b8442dd13e3244e0
3
+ metadata.gz: 4a01e8a21951f3b998cfae0253c4d35c5412ac39f94681283b9059d4a196651e
4
+ data.tar.gz: 5b4a5bb02d86b27391b005acb329893025222c20e9d0c99b88ed0d7e6322dc5e
5
5
  SHA512:
6
- metadata.gz: 2c98c3ea7e22d0b023a328207c9599dca0f74b54d5ed05eea04378c5c2ed5a2803799b7dcafa54d041d3b7dac8b3e08c8559d4cacb03b2419796368fa153401e
7
- data.tar.gz: 36c0faa98a5f6657a88ce770924e2669f91fcea2985b38f1c82dc0766d0c5a5cf6a4b0342d4684f5b679b41dc76f5fa94b791a6468b6f0514c28f6d70864b41a
6
+ metadata.gz: 8972eafdb4c041a61258f409fef5a93dc0ff56b70e5c8dd384e126652c1ab32a4e52dc56bd253d7c204ea6313e3ac321e93c7245c722c0d5491b59443c63435a
7
+ data.tar.gz: 4a5dfb958b61b78cdcf9312250d3ba00182894241da1c3e9114f08f1029fb174be07043f607a864986e3d09d6d308dfac4d13549e2483d5746235b34c4dd05a2
data/CHANGELOG.md CHANGED
@@ -1,4 +1,7 @@
1
- ## [Unreleased]
1
+ ## [0.10.0] - 2023-10-08
2
+
3
+ - Add media upload helper methods (6c6a267)
4
+ - Add PayloadTooLargeError class (cd61850)
2
5
 
3
6
  ## [0.9.1] - 2023-10-06
4
7
 
data/lib/x/client.rb CHANGED
@@ -19,6 +19,9 @@ module X
19
19
  def_delegators :@request_builder, :content_type=, :user_agent=
20
20
  def_delegators :@response_handler, :array_class, :object_class
21
21
  def_delegators :@response_handler, :array_class=, :object_class=
22
+ alias_method :base_url, :base_uri
23
+ alias_method :base_url=, :base_uri=
24
+ attr_accessor :authenticator, :connection, :request_builder, :redirect_handler, :response_handler
22
25
 
23
26
  def initialize(bearer_token: nil,
24
27
  api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
data/lib/x/connection.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "forwardable"
2
2
  require "net/http"
3
+ require "openssl"
3
4
  require "uri"
4
5
  require_relative "errors/network_error"
5
6
 
@@ -16,8 +17,10 @@ module X
16
17
  DEFAULT_WRITE_TIMEOUT = 60 # seconds
17
18
  NETWORK_ERRORS = [
18
19
  Errno::ECONNREFUSED,
20
+ Errno::ECONNRESET,
19
21
  Net::OpenTimeout,
20
- Net::ReadTimeout
22
+ Net::ReadTimeout,
23
+ OpenSSL::SSL::SSLError
21
24
  ].freeze
22
25
 
23
26
  attr_reader :base_uri, :proxy_uri, :http_client
@@ -39,9 +42,9 @@ module X
39
42
  end
40
43
 
41
44
  def send_request(request)
42
- @http_client.request(request)
45
+ response = @http_client.request(request)
43
46
  rescue *NETWORK_ERRORS => e
44
- raise NetworkError, "Network error: #{e.message}"
47
+ raise NetworkError.new("Network error: #{e.message}", response: response)
45
48
  end
46
49
 
47
50
  def base_uri=(base_url)
@@ -56,6 +59,17 @@ module X
56
59
  @http_client.instance_variable_get(:@debug_output)
57
60
  end
58
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
+ }
71
+ end
72
+
59
73
  private
60
74
 
61
75
  def apply_http_client_settings(open_timeout:, read_timeout:, write_timeout:, debug_output:)
@@ -84,15 +98,15 @@ module X
84
98
  end
85
99
 
86
100
  def build_http_client(host:, port:)
87
- if @proxy_uri.nil?
88
- Net::HTTP.new(host, port)
89
- else
101
+ if defined?(@proxy_uri)
90
102
  Net::HTTP.new(host, port, @proxy_uri&.host, @proxy_uri&.port, @proxy_uri&.user, @proxy_uri&.password)
103
+ else
104
+ Net::HTTP.new(host, port)
91
105
  end
92
106
  end
93
107
 
94
108
  def conditionally_apply_http_client_settings
95
- if @http_client
109
+ if defined?(@http_client)
96
110
  settings = current_http_client_settings
97
111
  yield
98
112
  apply_http_client_settings(**settings)
@@ -1,15 +1,12 @@
1
1
  require "json"
2
- require "net/http"
3
2
 
4
3
  module X
5
4
  # Base error class
6
5
  class Error < ::StandardError
7
- JSON_CONTENT_TYPE_REGEXP = %r{application/(problem\+|)json}
8
-
9
6
  attr_reader :object
10
7
 
11
8
  def initialize(msg, response:)
12
- @object = JSON.parse(response.body || "{}") if JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
9
+ @object = JSON.parse(response.body) if response&.body && !response.body.empty?
13
10
  super(msg)
14
11
  end
15
12
  end
@@ -0,0 +1,5 @@
1
+ require_relative "server_error"
2
+
3
+ module X
4
+ class InternalServerError < ServerError; end
5
+ end
@@ -1,5 +1,5 @@
1
- require_relative "error"
1
+ require_relative "server_error"
2
2
 
3
3
  module X
4
- class NetworkError < StandardError; end
4
+ class NetworkError < Error; end
5
5
  end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module X
4
+ class PayloadTooLargeError < ClientError; end
5
+ end
@@ -0,0 +1,125 @@
1
+ require "securerandom"
2
+
3
+ module X
4
+ # Helper module for uploading images and videos
5
+ module MediaUpload
6
+ extend self
7
+
8
+ MAX_RETRIES = 3
9
+ BYTES_PER_MB = 1_048_576
10
+ MEDIA_CATEGORIES = %w[dm_gif dm_image dm_video subtitles tweet_gif tweet_image tweet_video].freeze
11
+ DM_GIF, DM_IMAGE, DM_VIDEO, SUBTITLES, TWEET_GIF, TWEET_IMAGE, TWEET_VIDEO = MEDIA_CATEGORIES
12
+ DEFAULT_MIME_TYPE = "application/octet-stream".freeze
13
+ MIME_TYPES = %w[image/gif image/jpeg video/mp4 image/png application/x-subrip image/webp].freeze
14
+ GIF_MIME_TYPE, JPEG_MIME_TYPE, MP4_MIME_TYPE, PNG_MIME_TYPE, SUBRIP_MIME_TYPE, WEBP_MIME_TYPE = MIME_TYPES
15
+ MIME_TYPE_MAP = {"gif" => GIF_MIME_TYPE, "jpg" => JPEG_MIME_TYPE, "jpeg" => JPEG_MIME_TYPE, "mp4" => MP4_MIME_TYPE,
16
+ "png" => PNG_MIME_TYPE, "srt" => SUBRIP_MIME_TYPE, "webp" => WEBP_MIME_TYPE}.freeze
17
+
18
+ def media_upload(client:, file_path:, media_category:, media_type: infer_media_type(file_path, media_category),
19
+ boundary: SecureRandom.hex)
20
+ validate!(file_path: file_path, media_category: media_category)
21
+ upload_client = client.dup.tap { |c| c.base_uri = "https://upload.twitter.com/1.1/" }
22
+ upload_body = construct_upload_body(file_path, media_type, boundary)
23
+ upload_client.content_type = "multipart/form-data, boundary=#{boundary}"
24
+ upload_client.post("media/upload.json?media_category=#{media_category}", upload_body)
25
+ end
26
+
27
+ def chunked_media_upload(client:, file_path:, media_category:, media_type: infer_media_type(file_path,
28
+ media_category), boundary: SecureRandom.hex, chunk_size_mb: 8)
29
+ validate!(file_path: file_path, media_category: media_category)
30
+ upload_client = client.dup.tap { |c| c.base_uri = "https://upload.twitter.com/1.1/" }
31
+ media = init(upload_client, file_path, media_type, media_category)
32
+ 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)
35
+ upload_client.post("media/upload.json?command=FINALIZE&media_id=#{media["media_id"]}")
36
+ end
37
+
38
+ def await_processing(client:, media:)
39
+ upload_client = client.dup.tap { |c| c.base_uri = "https://upload.twitter.com/1.1/" }
40
+ loop do
41
+ status = upload_client.get("media/upload.json?command=STATUS&media_id=#{media["media_id"]}")
42
+ return status if status["processing_info"]["state"] == "succeeded"
43
+
44
+ sleep status["processing_info"]["check_after_secs"].to_i
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def validate!(file_path:, media_category:)
51
+ raise "File not found: #{file_path}" unless File.exist?(file_path)
52
+
53
+ return if MEDIA_CATEGORIES.include?(media_category.downcase)
54
+
55
+ raise ArgumentError, "Invalid media_category: #{media_category}. Valid values: #{MEDIA_CATEGORIES.join(", ")}"
56
+ end
57
+
58
+ def infer_media_type(file_path, media_category)
59
+ case media_category.downcase
60
+ when TWEET_GIF, DM_GIF then GIF_MIME_TYPE
61
+ when TWEET_VIDEO, DM_VIDEO then MP4_MIME_TYPE
62
+ when SUBTITLES then SUBRIP_MIME_TYPE
63
+ else MIME_TYPE_MAP[File.extname(file_path).delete(".").downcase] || DEFAULT_MIME_TYPE
64
+ end
65
+ end
66
+
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
+ def split(file_path, chunk_size)
74
+ file_number = -1
75
+
76
+ [].tap do |chunk_paths|
77
+ File.open(file_path, "rb") do |f|
78
+ while (chunk = f.read(chunk_size))
79
+ chunk_paths << "#{Dir.mktmpdir}/x#{format("%03d", file_number += 1)}".tap do |path|
80
+ File.write(path, chunk)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def append(upload_client, chunk_paths, media, media_type, boundary = SecureRandom.hex)
88
+ threads = chunk_paths.map.with_index do |chunk_path, index|
89
+ Thread.new do
90
+ upload_body = construct_upload_body(chunk_path, media_type, boundary)
91
+ query = "command=APPEND&media_id=#{media["media_id"]}&segment_index=#{index}"
92
+ upload_chunk(upload_client, query, upload_body, chunk_path, boundary)
93
+ end
94
+ end
95
+ threads.each(&:join)
96
+ end
97
+
98
+ def upload_chunk(upload_client, query, upload_body, chunk_path, boundary)
99
+ # Initialize a new client to avoid shared connection issues
100
+ client = upload_client.dup
101
+ client.connection = Connection.new(**upload_client.connection.configuration.merge(base_url: "https://upload.twitter.com/1.1/"))
102
+ client.content_type = "multipart/form-data, boundary=#{boundary}"
103
+ client.post("media/upload.json?#{query}", upload_body)
104
+ rescue NetworkError, ServerError
105
+ retries ||= 0
106
+ ((retries += 1) < MAX_RETRIES) ? retry : raise
107
+ ensure
108
+ cleanup_chunk(chunk_path)
109
+ end
110
+
111
+ def cleanup_chunk(chunk_path)
112
+ dirname = File.dirname(chunk_path)
113
+ File.delete(chunk_path)
114
+ Dir.delete(dirname) if Dir.empty?(dirname)
115
+ end
116
+
117
+ def construct_upload_body(file_path, media_type, boundary = SecureRandom.hex)
118
+ "--#{boundary}\r\n" \
119
+ "Content-Disposition: form-data; name=\"media\"; filename=\"#{File.basename(file_path)}\"\r\n" \
120
+ "Content-Type: #{media_type}\r\n\r\n" \
121
+ "#{File.read(file_path)}\r\n" \
122
+ "--#{boundary}--\r\n"
123
+ end
124
+ end
125
+ end
@@ -47,9 +47,7 @@ module X
47
47
  end
48
48
 
49
49
  def send_new_request(new_uri, new_request)
50
- @connection = Connection.new(base_url: new_uri, open_timeout: connection.open_timeout,
51
- read_timeout: connection.read_timeout, write_timeout: connection.write_timeout,
52
- debug_output: connection.debug_output, proxy_url: connection.proxy_uri)
50
+ @connection = Connection.new(**connection.configuration.merge(base_url: new_uri))
53
51
  connection.send_request(new_request)
54
52
  end
55
53
  end
@@ -32,6 +32,13 @@ module X
32
32
  request
33
33
  end
34
34
 
35
+ def configuration
36
+ {
37
+ content_type: content_type,
38
+ user_agent: user_agent
39
+ }
40
+ end
41
+
35
42
  private
36
43
 
37
44
  def create_request(http_method, uri, body)
@@ -1,12 +1,13 @@
1
1
  require "json"
2
2
  require "net/http"
3
- require_relative "errors/bad_request_error"
4
3
  require_relative "errors/authentication_error"
4
+ require_relative "errors/bad_request_error"
5
5
  require_relative "errors/forbidden_error"
6
6
  require_relative "errors/not_found_error"
7
- require_relative "errors/too_many_requests_error"
8
- require_relative "errors/server_error"
7
+ require_relative "errors/payload_too_large_error"
8
+ require_relative "errors/internal_server_error"
9
9
  require_relative "errors/service_unavailable_error"
10
+ require_relative "errors/too_many_requests_error"
10
11
 
11
12
  module X
12
13
  # Process HTTP responses
@@ -18,8 +19,9 @@ module X
18
19
  401 => AuthenticationError,
19
20
  403 => ForbiddenError,
20
21
  404 => NotFoundError,
22
+ 413 => PayloadTooLargeError,
21
23
  429 => TooManyRequestsError,
22
- 500 => ServerError,
24
+ 500 => InternalServerError,
23
25
  503 => ServiceUnavailableError
24
26
  }.freeze
25
27
  JSON_CONTENT_TYPE_REGEXP = %r{application/(problem\+|)json}
@@ -41,6 +43,13 @@ module X
41
43
  end
42
44
  end
43
45
 
46
+ def configuration
47
+ {
48
+ array_class: array_class,
49
+ object_class: object_class
50
+ }
51
+ end
52
+
44
53
  private
45
54
 
46
55
  def success?(response)
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.9.1")
4
+ VERSION = Gem::Version.create("0.10.0")
5
5
  end
data/sig/x.rbs CHANGED
@@ -36,8 +36,8 @@ module X
36
36
  class Error < StandardError
37
37
  JSON_CONTENT_TYPE_REGEXP: Regexp
38
38
 
39
- attr_reader object: Hash[String, untyped]
40
- def initialize: (String msg, response: Net::HTTPResponse) -> void
39
+ attr_reader object: untyped
40
+ def initialize: (String msg, response: Net::HTTPResponse?) -> void
41
41
 
42
42
  private
43
43
  def json_response?: (Net::HTTPResponse response) -> bool
@@ -55,9 +55,15 @@ module X
55
55
  class ForbiddenError < ClientError
56
56
  end
57
57
 
58
+ class InternalServerError < ServerError
59
+ end
60
+
58
61
  class NotFoundError < ClientError
59
62
  end
60
63
 
64
+ class PayloadTooLargeError < ClientError
65
+ end
66
+
61
67
  class TooManyRedirectsError < ClientError
62
68
  end
63
69
 
@@ -87,7 +93,8 @@ module X
87
93
  DEFAULT_OPEN_TIMEOUT: Integer
88
94
  DEFAULT_READ_TIMEOUT: Integer
89
95
  DEFAULT_WRITE_TIMEOUT: Integer
90
- NETWORK_ERRORS: Array[(singleton(::Errno::ECONNREFUSED) | singleton(::Net::OpenTimeout) | singleton(::Net::ReadTimeout))]
96
+ NETWORK_ERRORS: Array[(singleton(Errno::ECONNREFUSED) | singleton(Errno::ECONNRESET) | singleton(Net::OpenTimeout) | singleton(Net::ReadTimeout) | singleton(OpenSSL::SSL::SSLError))]
97
+
91
98
  extend Forwardable
92
99
  @http_client: Net::HTTP
93
100
 
@@ -100,6 +107,7 @@ module X
100
107
  def send_request: (Net::HTTPRequest request) -> Net::HTTPResponse
101
108
  def base_uri=: (URI::Generic | String base_url) -> void
102
109
  def debug_output: -> IO?
110
+ def configuration: -> Hash[Symbol, untyped]
103
111
 
104
112
  private
105
113
  def apply_http_client_settings: (open_timeout: Float | Integer, read_timeout: Float | Integer, write_timeout: Float | Integer, debug_output: IO?) -> untyped
@@ -121,6 +129,7 @@ module X
121
129
  attr_accessor user_agent: String
122
130
  def initialize: (?content_type: String, ?user_agent: String) -> void
123
131
  def build: (BearerTokenAuthenticator | OauthAuthenticator authenticator, Symbol http_method, URI::Generic uri, ?body: String?) -> (Net::HTTPRequest)
132
+ def configuration: -> Hash[Symbol, untyped]
124
133
 
125
134
  private
126
135
  def create_request: (Symbol http_method, URI::Generic uri, String? body) -> (Net::HTTPRequest)
@@ -149,13 +158,14 @@ module X
149
158
  class ResponseHandler
150
159
  DEFAULT_ARRAY_CLASS: Class
151
160
  DEFAULT_OBJECT_CLASS: Class
152
- ERROR_CLASSES: Hash[Integer, singleton(AuthenticationError) | singleton(BadRequestError) | singleton(ForbiddenError) | singleton(NotFoundError) | singleton(ServerError) | singleton(ServiceUnavailableError) | singleton(TooManyRequestsError)]
161
+ ERROR_CLASSES: Hash[Integer, singleton(AuthenticationError) | singleton(BadRequestError) | singleton(ForbiddenError) | singleton(InternalServerError) | singleton(NotFoundError) | singleton(PayloadTooLargeError) | singleton(ServiceUnavailableError) | singleton(TooManyRequestsError)]
153
162
  JSON_CONTENT_TYPE_REGEXP: Regexp
154
163
 
155
164
  attr_accessor array_class: Class
156
165
  attr_accessor object_class: Class
157
166
  def initialize: (?array_class: Class, ?object_class: Class) -> void
158
- def handle: (Net::HTTPResponse response) -> Hash[String, untyped]?
167
+ def handle: (Net::HTTPResponse response) -> untyped
168
+ def configuration: -> Hash[Symbol, Class]
159
169
 
160
170
  private
161
171
  def success?: (Net::HTTPResponse response) -> bool
@@ -170,15 +180,80 @@ module X
170
180
  @redirect_handler: RedirectHandler
171
181
  @response_handler: ResponseHandler
172
182
 
173
- attr_reader base_uri: URI::Generic
183
+ attr_accessor access_token: String
184
+ attr_accessor access_token_secret: String
185
+ attr_accessor api_key: String
186
+ attr_accessor api_key_secret: String
187
+ attr_accessor bearer_token: String
188
+ attr_accessor base_uri: URI::Generic | String
189
+ attr_accessor open_timeout: Float | Integer
190
+ attr_accessor read_timeout: Float | Integer
191
+ attr_accessor write_timeout: Float | Integer
192
+ attr_accessor proxy_url: String
193
+ attr_accessor content_type: String
194
+ attr_accessor user_agent: String
195
+ attr_accessor debug_output: IO?
196
+ attr_accessor array_class: Class
197
+ attr_accessor object_class: Class
198
+ attr_accessor max_redirects: Integer
199
+ attr_accessor authenticator: BearerTokenAuthenticator | OauthAuthenticator
200
+ attr_accessor connection: Connection
201
+ attr_accessor request_builder: RequestBuilder
202
+ attr_accessor redirect_handler: RedirectHandler
203
+ attr_accessor response_handler: ResponseHandler
204
+ alias base_url base_uri
205
+
206
+
174
207
  def initialize: (?bearer_token: String?, ?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?base_url: URI::Generic | String, ?content_type: String, ?user_agent: 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
175
- def get: (String endpoint) -> Hash[String, untyped]?
176
- def post: (String endpoint, ?nil body) -> Hash[String, untyped]?
177
- def put: (String endpoint, ?nil body) -> Hash[String, untyped]?
178
- def delete: (String endpoint) -> Hash[String, untyped]?
208
+ def get: (String endpoint) -> untyped
209
+ def post: (String endpoint, ?String? body) -> untyped
210
+ def put: (String endpoint, ?String? body) -> untyped
211
+ def delete: (String endpoint) -> untyped
179
212
 
180
213
  private
181
214
  def initialize_authenticator: (String? bearer_token, String? api_key, String? api_key_secret, String? access_token, String? access_token_secret) -> (BearerTokenAuthenticator | OauthAuthenticator)
182
- def send_request: (Symbol http_method, String endpoint, ?nil body) -> Hash[String, untyped]?
215
+ def send_request: (Symbol http_method, String endpoint, ?String? body) -> untyped
183
216
  end
217
+
218
+ module MediaUpload
219
+ MAX_RETRIES: Integer
220
+ BYTES_PER_MB: Integer
221
+ MEDIA_CATEGORIES: Array[String]
222
+ DM_GIF: String
223
+ DM_IMAGE: String
224
+ DM_VIDEO: String
225
+ SUBTITLES: String
226
+ TWEET_GIF: String
227
+ TWEET_IMAGE: String
228
+ TWEET_VIDEO: String
229
+ DEFAULT_MIME_TYPE: String
230
+ MIME_TYPES: Array[String]
231
+ GIF_MIME_TYPE: String
232
+ JPEG_MIME_TYPE: String
233
+ MP4_MIME_TYPE: String
234
+ PNG_MIME_TYPE: String
235
+ SUBRIP_MIME_TYPE: String
236
+ WEBP_MIME_TYPE: String
237
+ MIME_TYPE_MAP: Hash[String, String]
238
+ extend MediaUpload
239
+
240
+ def media_upload: (client: Client, file_path: String, media_category: String, ?media_type: String, ?boundary: String) -> untyped
241
+ def chunked_media_upload: (client: Client, file_path: String, media_category: String, ?media_type: String, ?boundary: String, ?chunk_size_mb: Integer) -> untyped
242
+ def await_processing: (client: Client, media: untyped) -> untyped
243
+
244
+ private
245
+ def validate!: (file_path: String, media_category: String) -> nil
246
+ def infer_media_type: (String file_path, String media_category) -> String
247
+ def init: (Client upload_client, String file_path, String media_type, String media_category) -> untyped
248
+ def split: (String file_path, Integer chunk_size) -> Array[String]
249
+ def append: (Client upload_client, Array[String] chunk_paths, untyped media, String media_type, ?String boundary) -> Array[String]
250
+ def upload_chunk: (Client upload_client, String query, String chunk_path, String media_type, String boundary) -> Integer?
251
+ def cleanup_chunk: (String chunk_path) -> Integer?
252
+ def finalize: (Client upload_client, untyped media) -> untyped
253
+ def construct_upload_body: (String file_path, String media_type, ?String boundary) -> String
254
+ end
255
+ end
256
+
257
+ class Dir
258
+ def self.mktmpdir: (?String? prefix_suffix) -> String
184
259
  end
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.9.1
4
+ version: 0.10.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-06 00:00:00.000000000 Z
11
+ date: 2023-10-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -31,12 +31,15 @@ files:
31
31
  - lib/x/errors/client_error.rb
32
32
  - lib/x/errors/error.rb
33
33
  - lib/x/errors/forbidden_error.rb
34
+ - lib/x/errors/internal_server_error.rb
34
35
  - lib/x/errors/network_error.rb
35
36
  - lib/x/errors/not_found_error.rb
37
+ - lib/x/errors/payload_too_large_error.rb
36
38
  - lib/x/errors/server_error.rb
37
39
  - lib/x/errors/service_unavailable_error.rb
38
40
  - lib/x/errors/too_many_redirects_error.rb
39
41
  - lib/x/errors/too_many_requests_error.rb
42
+ - lib/x/media_upload.rb
40
43
  - lib/x/oauth_authenticator.rb
41
44
  - lib/x/redirect_handler.rb
42
45
  - lib/x/request_builder.rb