x 0.19.0 → 1.0.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/.yardopts +9 -0
- data/CHANGELOG.md +648 -15
- data/README.md +506 -73
- data/SECURITY.md +66 -0
- data/UPGRADING.md +670 -0
- data/lib/x/version.rb +16 -1
- data/lib/x.rb +13 -1
- data/sig/manifest.yaml +5 -0
- data/sig/x.rbs +5 -361
- metadata +57 -49
- data/bin/console +0 -10
- data/bin/setup +0 -6
- data/lib/x/account_uploader.rb +0 -168
- data/lib/x/authenticator.rb +0 -21
- data/lib/x/bearer_token_authenticator.rb +0 -36
- data/lib/x/client.rb +0 -191
- data/lib/x/client_credentials.rb +0 -208
- data/lib/x/connection.rb +0 -183
- data/lib/x/errors/bad_gateway.rb +0 -6
- data/lib/x/errors/bad_request.rb +0 -6
- data/lib/x/errors/client_error.rb +0 -6
- data/lib/x/errors/connection_exception.rb +0 -6
- data/lib/x/errors/error.rb +0 -4
- data/lib/x/errors/forbidden.rb +0 -6
- data/lib/x/errors/gateway_timeout.rb +0 -6
- data/lib/x/errors/gone.rb +0 -6
- data/lib/x/errors/http_error.rb +0 -84
- data/lib/x/errors/internal_server_error.rb +0 -6
- data/lib/x/errors/invalid_media_type.rb +0 -6
- data/lib/x/errors/network_error.rb +0 -6
- data/lib/x/errors/not_acceptable.rb +0 -6
- data/lib/x/errors/not_found.rb +0 -6
- data/lib/x/errors/payload_too_large.rb +0 -6
- data/lib/x/errors/server_error.rb +0 -6
- data/lib/x/errors/service_unavailable.rb +0 -6
- data/lib/x/errors/too_many_redirects.rb +0 -6
- data/lib/x/errors/too_many_requests.rb +0 -58
- data/lib/x/errors/unauthorized.rb +0 -6
- data/lib/x/errors/unprocessable_entity.rb +0 -6
- data/lib/x/media_upload_validator.rb +0 -36
- data/lib/x/media_uploader.rb +0 -234
- data/lib/x/oauth2_authenticator.rb +0 -169
- data/lib/x/oauth_authenticator.rb +0 -182
- data/lib/x/rate_limit.rb +0 -89
- data/lib/x/redirect_handler.rb +0 -109
- data/lib/x/request_builder.rb +0 -93
- data/lib/x/response_parser.rb +0 -81
- data/lib/x/stream_parser.rb +0 -75
data/lib/x/account_uploader.rb
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
require "base64"
|
|
2
|
-
require "securerandom"
|
|
3
|
-
require_relative "errors/invalid_media_type"
|
|
4
|
-
|
|
5
|
-
module X
|
|
6
|
-
# Uploads profile images and banners to the X API v1.1
|
|
7
|
-
# @api public
|
|
8
|
-
module AccountUploader
|
|
9
|
-
extend self
|
|
10
|
-
|
|
11
|
-
# Base URL for X API v1.1 account endpoints
|
|
12
|
-
V1_BASE_URL = "https://api.x.com/1.1/".freeze
|
|
13
|
-
# Supported image extensions for profile uploads
|
|
14
|
-
SUPPORTED_EXTENSIONS = %w[gif jpg jpeg png].freeze
|
|
15
|
-
# Mapping of file extensions to MIME types
|
|
16
|
-
MIME_TYPE_MAP = {"gif" => "image/gif", "jpg" => "image/jpeg", "jpeg" => "image/jpeg", "png" => "image/png"}.freeze
|
|
17
|
-
|
|
18
|
-
# Update the authenticating user's profile image
|
|
19
|
-
#
|
|
20
|
-
# @api public
|
|
21
|
-
# @param client [Client] the X API client
|
|
22
|
-
# @param file_path [String] the path to the image file
|
|
23
|
-
# @param boundary [String] the multipart boundary
|
|
24
|
-
# @return [Hash, nil] the updated user object
|
|
25
|
-
# @raise [RuntimeError] if the file does not exist
|
|
26
|
-
# @raise [InvalidMediaType] if the file type is not supported
|
|
27
|
-
# @example Update profile image from a file
|
|
28
|
-
# AccountUploader.update_profile_image(client: client, file_path: "avatar.png")
|
|
29
|
-
def update_profile_image(client:, file_path:, boundary: SecureRandom.hex)
|
|
30
|
-
validate_file!(file_path)
|
|
31
|
-
upload_profile_image_binary(client:, content: File.binread(file_path), boundary:)
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Update the authenticating user's profile image from binary content
|
|
35
|
-
#
|
|
36
|
-
# @api public
|
|
37
|
-
# @param client [Client] the X API client
|
|
38
|
-
# @param content [String] the binary image content
|
|
39
|
-
# @param boundary [String] the multipart boundary
|
|
40
|
-
# @return [Hash, nil] the updated user object
|
|
41
|
-
# @example Update profile image from binary content
|
|
42
|
-
# AccountUploader.upload_profile_image_binary(client: client, content: image_data)
|
|
43
|
-
def upload_profile_image_binary(client:, content:, boundary: SecureRandom.hex)
|
|
44
|
-
body = construct_multipart_body(field_name: "image", content:, boundary:)
|
|
45
|
-
headers = {"Content-Type" => "multipart/form-data; boundary=#{boundary}"}
|
|
46
|
-
v1_client(client).post("account/update_profile_image.json", body, headers:)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Update the authenticating user's profile banner
|
|
50
|
-
#
|
|
51
|
-
# @api public
|
|
52
|
-
# @param client [Client] the X API client
|
|
53
|
-
# @param file_path [String] the path to the image file
|
|
54
|
-
# @param width [Integer, nil] the width of the banner
|
|
55
|
-
# @param height [Integer, nil] the height of the banner
|
|
56
|
-
# @param offset_left [Integer, nil] the left offset of the banner
|
|
57
|
-
# @param offset_top [Integer, nil] the top offset of the banner
|
|
58
|
-
# @param boundary [String] the multipart boundary
|
|
59
|
-
# @return [Hash, nil] nil on success (204 No Content)
|
|
60
|
-
# @raise [RuntimeError] if the file does not exist
|
|
61
|
-
# @raise [InvalidMediaType] if the file type is not supported
|
|
62
|
-
# @example Update profile banner from a file
|
|
63
|
-
# AccountUploader.update_profile_banner(client: client, file_path: "banner.png")
|
|
64
|
-
# @example Update profile banner with dimensions
|
|
65
|
-
# AccountUploader.update_profile_banner(client: client, file_path: "banner.png", width: 1500, height: 500)
|
|
66
|
-
def update_profile_banner(client:, file_path:, width: nil, height: nil, offset_left: nil, offset_top: nil,
|
|
67
|
-
boundary: SecureRandom.hex)
|
|
68
|
-
validate_file!(file_path)
|
|
69
|
-
upload_profile_banner_binary(client:, content: File.binread(file_path), width:, height:, offset_left:,
|
|
70
|
-
offset_top:, boundary:)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Update the authenticating user's profile banner from binary content
|
|
74
|
-
#
|
|
75
|
-
# @api public
|
|
76
|
-
# @param client [Client] the X API client
|
|
77
|
-
# @param content [String] the binary image content
|
|
78
|
-
# @param width [Integer, nil] the width of the banner
|
|
79
|
-
# @param height [Integer, nil] the height of the banner
|
|
80
|
-
# @param offset_left [Integer, nil] the left offset of the banner
|
|
81
|
-
# @param offset_top [Integer, nil] the top offset of the banner
|
|
82
|
-
# @param boundary [String] the multipart boundary
|
|
83
|
-
# @return [Hash, nil] nil on success (204 No Content)
|
|
84
|
-
# @example Update profile banner from binary content
|
|
85
|
-
# AccountUploader.upload_profile_banner_binary(client: client, content: image_data)
|
|
86
|
-
def upload_profile_banner_binary(client:, content:, width: nil, height: nil, offset_left: nil, offset_top: nil,
|
|
87
|
-
boundary: SecureRandom.hex)
|
|
88
|
-
body = construct_banner_body(content:, width:, height:, offset_left:, offset_top:, boundary:)
|
|
89
|
-
headers = {"Content-Type" => "multipart/form-data; boundary=#{boundary}"}
|
|
90
|
-
v1_client(client).post("account/update_profile_banner.json", body, headers:)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
|
|
95
|
-
# Create a v1.1 API client from an existing client
|
|
96
|
-
# @api private
|
|
97
|
-
# @param client [Client] the original client
|
|
98
|
-
# @return [Client] a client configured for v1.1 API
|
|
99
|
-
def v1_client(client)
|
|
100
|
-
Client.new(
|
|
101
|
-
api_key: client.api_key,
|
|
102
|
-
api_key_secret: client.api_key_secret,
|
|
103
|
-
access_token: client.access_token,
|
|
104
|
-
access_token_secret: client.access_token_secret,
|
|
105
|
-
base_url: V1_BASE_URL
|
|
106
|
-
)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Validate that the file exists and has a supported extension
|
|
110
|
-
# @api private
|
|
111
|
-
# @param file_path [String] the file path
|
|
112
|
-
# @return [nil]
|
|
113
|
-
# @raise [RuntimeError] if the file does not exist
|
|
114
|
-
# @raise [InvalidMediaType] if the file type is not supported
|
|
115
|
-
def validate_file!(file_path)
|
|
116
|
-
raise "File not found: #{file_path}" unless File.exist?(file_path)
|
|
117
|
-
|
|
118
|
-
extension = File.extname(file_path).delete(".").downcase
|
|
119
|
-
return if SUPPORTED_EXTENSIONS.include?(extension)
|
|
120
|
-
|
|
121
|
-
raise InvalidMediaType, "Unsupported file type: #{extension}. Supported types: #{SUPPORTED_EXTENSIONS.join(", ")}"
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Construct multipart body for profile image upload
|
|
125
|
-
# @api private
|
|
126
|
-
# @param field_name [String] the form field name
|
|
127
|
-
# @param content [String] the binary content
|
|
128
|
-
# @param boundary [String] the multipart boundary
|
|
129
|
-
# @return [String] the multipart body
|
|
130
|
-
def construct_multipart_body(field_name:, content:, boundary:)
|
|
131
|
-
"--#{boundary}\r\n" \
|
|
132
|
-
"Content-Disposition: form-data; name=\"#{field_name}\"\r\n" \
|
|
133
|
-
"Content-Type: application/octet-stream\r\n\r\n" \
|
|
134
|
-
"#{content}\r\n" \
|
|
135
|
-
"--#{boundary}--\r\n"
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Construct multipart body for profile banner upload with optional dimensions
|
|
139
|
-
# @api private
|
|
140
|
-
# @param content [String] the binary content
|
|
141
|
-
# @param width [Integer, nil] the width
|
|
142
|
-
# @param height [Integer, nil] the height
|
|
143
|
-
# @param offset_left [Integer, nil] the left offset
|
|
144
|
-
# @param offset_top [Integer, nil] the top offset
|
|
145
|
-
# @param boundary [String] the multipart boundary
|
|
146
|
-
# @return [String] the multipart body
|
|
147
|
-
def construct_banner_body(content:, width:, height:, offset_left:, offset_top:, boundary:)
|
|
148
|
-
body = ""
|
|
149
|
-
body += multipart_field("width", width, boundary) if width
|
|
150
|
-
body += multipart_field("height", height, boundary) if height
|
|
151
|
-
body += multipart_field("offset_left", offset_left, boundary) if offset_left
|
|
152
|
-
body += multipart_field("offset_top", offset_top, boundary) if offset_top
|
|
153
|
-
body + construct_multipart_body(field_name: "banner", content:, boundary:)
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Create a multipart form field
|
|
157
|
-
# @api private
|
|
158
|
-
# @param name [String] the field name
|
|
159
|
-
# @param value [Object] the field value
|
|
160
|
-
# @param boundary [String] the multipart boundary
|
|
161
|
-
# @return [String] the multipart field
|
|
162
|
-
def multipart_field(name, value, boundary)
|
|
163
|
-
"--#{boundary}\r\n" \
|
|
164
|
-
"Content-Disposition: form-data; name=\"#{name}\"\r\n\r\n" \
|
|
165
|
-
"#{value}\r\n"
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
data/lib/x/authenticator.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# A Ruby client for the X API
|
|
2
|
-
module X
|
|
3
|
-
# Base class for authentication
|
|
4
|
-
# @api public
|
|
5
|
-
class Authenticator
|
|
6
|
-
# The HTTP header name for authentication
|
|
7
|
-
AUTHENTICATION_HEADER = "Authorization".freeze
|
|
8
|
-
|
|
9
|
-
# Generate the authentication header for a request
|
|
10
|
-
#
|
|
11
|
-
# @api public
|
|
12
|
-
# @param _request [Net::HTTPRequest] the HTTP request
|
|
13
|
-
# @return [Hash{String => String}] the authentication header
|
|
14
|
-
# @example Generate an empty authentication header
|
|
15
|
-
# authenticator = X::Authenticator.new
|
|
16
|
-
# authenticator.header(request)
|
|
17
|
-
def header(_request)
|
|
18
|
-
{AUTHENTICATION_HEADER => ""}
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
require_relative "authenticator"
|
|
2
|
-
|
|
3
|
-
module X
|
|
4
|
-
# Authenticator for Bearer token authentication
|
|
5
|
-
# @api public
|
|
6
|
-
class BearerTokenAuthenticator < Authenticator
|
|
7
|
-
# The bearer token for authentication
|
|
8
|
-
# @api public
|
|
9
|
-
# @return [String] the bearer token
|
|
10
|
-
# @example Get the bearer token
|
|
11
|
-
# authenticator.bearer_token
|
|
12
|
-
attr_accessor :bearer_token
|
|
13
|
-
|
|
14
|
-
# Initialize a new BearerTokenAuthenticator
|
|
15
|
-
#
|
|
16
|
-
# @api public
|
|
17
|
-
# @param bearer_token [String] the bearer token for authentication
|
|
18
|
-
# @return [BearerTokenAuthenticator] a new instance
|
|
19
|
-
# @example Create a new bearer token authenticator
|
|
20
|
-
# authenticator = X::BearerTokenAuthenticator.new(bearer_token: "token")
|
|
21
|
-
def initialize(bearer_token:)
|
|
22
|
-
@bearer_token = bearer_token
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Generate the authentication header for a request
|
|
26
|
-
#
|
|
27
|
-
# @api public
|
|
28
|
-
# @param _request [Net::HTTPRequest] the HTTP request
|
|
29
|
-
# @return [Hash{String => String}] the authentication header with bearer token
|
|
30
|
-
# @example Generate a bearer authentication header
|
|
31
|
-
# authenticator.header(request)
|
|
32
|
-
def header(_request)
|
|
33
|
-
{AUTHENTICATION_HEADER => "Bearer #{bearer_token}"}
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
data/lib/x/client.rb
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
require "forwardable"
|
|
2
|
-
require_relative "authenticator"
|
|
3
|
-
require_relative "bearer_token_authenticator"
|
|
4
|
-
require_relative "client_credentials"
|
|
5
|
-
require_relative "connection"
|
|
6
|
-
require_relative "oauth_authenticator"
|
|
7
|
-
require_relative "oauth2_authenticator"
|
|
8
|
-
require_relative "redirect_handler"
|
|
9
|
-
require_relative "request_builder"
|
|
10
|
-
require_relative "response_parser"
|
|
11
|
-
require_relative "stream_parser"
|
|
12
|
-
|
|
13
|
-
module X
|
|
14
|
-
# A client for interacting with the X API
|
|
15
|
-
# @api public
|
|
16
|
-
class Client
|
|
17
|
-
extend Forwardable
|
|
18
|
-
include ClientCredentials
|
|
19
|
-
|
|
20
|
-
# Default base URL for the X API
|
|
21
|
-
DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
|
|
22
|
-
# Default class for parsing JSON arrays
|
|
23
|
-
DEFAULT_ARRAY_CLASS = Array
|
|
24
|
-
# Default class for parsing JSON objects
|
|
25
|
-
DEFAULT_OBJECT_CLASS = Hash
|
|
26
|
-
|
|
27
|
-
# The base URL for API requests
|
|
28
|
-
# @api public
|
|
29
|
-
# @return [String] the base URL for API requests
|
|
30
|
-
# @example Get or set the base URL
|
|
31
|
-
# client.base_url = "https://api.twitter.com/1.1/"
|
|
32
|
-
attr_accessor :base_url
|
|
33
|
-
|
|
34
|
-
# The default class for parsing JSON arrays
|
|
35
|
-
# @api public
|
|
36
|
-
# @return [Class] the default class for parsing JSON arrays
|
|
37
|
-
# @example Get or set the default array class
|
|
38
|
-
# client.default_array_class = Set
|
|
39
|
-
attr_accessor :default_array_class
|
|
40
|
-
|
|
41
|
-
# The default class for parsing JSON objects
|
|
42
|
-
# @api public
|
|
43
|
-
# @return [Class] the default class for parsing JSON objects
|
|
44
|
-
# @example Get or set the default object class
|
|
45
|
-
# client.default_object_class = OpenStruct
|
|
46
|
-
attr_accessor :default_object_class
|
|
47
|
-
|
|
48
|
-
# The authenticator for API requests
|
|
49
|
-
# @api public
|
|
50
|
-
# @return [Authenticator] the authenticator instance
|
|
51
|
-
# @example Check if the OAuth 2.0 token has expired
|
|
52
|
-
# client.authenticator.token_expired?
|
|
53
|
-
attr_reader :authenticator
|
|
54
|
-
|
|
55
|
-
def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :proxy_url, :debug_output
|
|
56
|
-
def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :proxy_url=, :debug_output=
|
|
57
|
-
def_delegators :@redirect_handler, :max_redirects
|
|
58
|
-
def_delegators :@redirect_handler, :max_redirects=
|
|
59
|
-
|
|
60
|
-
# Initialize a new X API client
|
|
61
|
-
#
|
|
62
|
-
# @api public
|
|
63
|
-
# @param api_key [String, nil] the API key for OAuth 1.0a authentication
|
|
64
|
-
# @param api_key_secret [String, nil] the API key secret for OAuth 1.0a authentication
|
|
65
|
-
# @param access_token [String, nil] the access token for OAuth authentication
|
|
66
|
-
# @param access_token_secret [String, nil] the access token secret for OAuth 1.0a authentication
|
|
67
|
-
# @param bearer_token [String, nil] the bearer token for authentication
|
|
68
|
-
# @param client_id [String, nil] the OAuth 2.0 client ID
|
|
69
|
-
# @param client_secret [String, nil] the OAuth 2.0 client secret
|
|
70
|
-
# @param refresh_token [String, nil] the OAuth 2.0 refresh token
|
|
71
|
-
# @param base_url [String] the base URL for API requests
|
|
72
|
-
# @param open_timeout [Integer] the timeout for opening connections in seconds
|
|
73
|
-
# @param read_timeout [Integer] the timeout for reading responses in seconds
|
|
74
|
-
# @param write_timeout [Integer] the timeout for writing requests in seconds
|
|
75
|
-
# @param debug_output [IO] the IO object for debug output
|
|
76
|
-
# @param proxy_url [String, nil] the proxy URL for requests
|
|
77
|
-
# @param default_array_class [Class] the default class for parsing JSON arrays
|
|
78
|
-
# @param default_object_class [Class] the default class for parsing JSON objects
|
|
79
|
-
# @param max_redirects [Integer] the maximum number of redirects to follow
|
|
80
|
-
# @return [Client] a new client instance
|
|
81
|
-
# @example Create a client with bearer token authentication
|
|
82
|
-
# client = X::Client.new(bearer_token: "your_bearer_token")
|
|
83
|
-
# @example Create a client with OAuth 1.0a authentication
|
|
84
|
-
# client = X::Client.new(api_key: "key", api_key_secret: "secret", access_token: "token", access_token_secret: "token_secret")
|
|
85
|
-
def initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
|
|
86
|
-
bearer_token: nil, client_id: nil, client_secret: nil, refresh_token: nil,
|
|
87
|
-
base_url: DEFAULT_BASE_URL,
|
|
88
|
-
open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
|
|
89
|
-
read_timeout: Connection::DEFAULT_READ_TIMEOUT,
|
|
90
|
-
write_timeout: Connection::DEFAULT_WRITE_TIMEOUT,
|
|
91
|
-
debug_output: nil,
|
|
92
|
-
proxy_url: nil,
|
|
93
|
-
default_array_class: DEFAULT_ARRAY_CLASS,
|
|
94
|
-
default_object_class: DEFAULT_OBJECT_CLASS,
|
|
95
|
-
max_redirects: RedirectHandler::DEFAULT_MAX_REDIRECTS)
|
|
96
|
-
initialize_credentials(api_key:, api_key_secret:, access_token:, access_token_secret:, bearer_token:,
|
|
97
|
-
client_id:, client_secret:, refresh_token:)
|
|
98
|
-
initialize_authenticator
|
|
99
|
-
@base_url = base_url
|
|
100
|
-
initialize_default_classes(default_array_class:, default_object_class:)
|
|
101
|
-
@connection = Connection.new(open_timeout:, read_timeout:, write_timeout:, debug_output:, proxy_url:)
|
|
102
|
-
@request_builder = RequestBuilder.new
|
|
103
|
-
@redirect_handler = RedirectHandler.new(connection: @connection, request_builder: @request_builder, max_redirects:)
|
|
104
|
-
@response_parser = ResponseParser.new
|
|
105
|
-
@stream_parser = StreamParser.new
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Perform a GET request to the X API
|
|
109
|
-
#
|
|
110
|
-
# @api public
|
|
111
|
-
# @return [Hash, Array, nil] the parsed response body
|
|
112
|
-
# @example Get a user by username
|
|
113
|
-
# client.get("users/by/username/sferik")
|
|
114
|
-
def get(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class)
|
|
115
|
-
execute_request(:get, endpoint, headers:, array_class:, object_class:)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Perform a POST request to the X API
|
|
119
|
-
#
|
|
120
|
-
# @api public
|
|
121
|
-
# @return [Hash, Array, nil] the parsed response body
|
|
122
|
-
# @example Create a tweet
|
|
123
|
-
# client.post("tweets", '{"text": "Hello, World!"}')
|
|
124
|
-
def post(endpoint, body = nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
|
|
125
|
-
execute_request(:post, endpoint, body:, headers:, array_class:, object_class:)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Perform a PUT request to the X API
|
|
129
|
-
#
|
|
130
|
-
# @api public
|
|
131
|
-
# @return [Hash, Array, nil] the parsed response body
|
|
132
|
-
# @example Update a resource
|
|
133
|
-
# client.put("some/endpoint", '{"key": "value"}')
|
|
134
|
-
def put(endpoint, body = nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
|
|
135
|
-
execute_request(:put, endpoint, body:, headers:, array_class:, object_class:)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Perform a DELETE request to the X API
|
|
139
|
-
#
|
|
140
|
-
# @api public
|
|
141
|
-
# @return [Hash, Array, nil] the parsed response body
|
|
142
|
-
# @example Delete a tweet
|
|
143
|
-
# client.delete("tweets/1234567890")
|
|
144
|
-
def delete(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class)
|
|
145
|
-
execute_request(:delete, endpoint, headers:, array_class:, object_class:)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
# Stream data from the X API
|
|
149
|
-
#
|
|
150
|
-
# @api public
|
|
151
|
-
# @param endpoint [String] the streaming API endpoint
|
|
152
|
-
# @param headers [Hash] additional headers for the request
|
|
153
|
-
# @param array_class [Class] the class for parsing JSON arrays
|
|
154
|
-
# @param object_class [Class] the class for parsing JSON objects
|
|
155
|
-
# @yield [Hash, Array] each parsed JSON object from the stream
|
|
156
|
-
# @return [void]
|
|
157
|
-
# @raise [HTTPError] if the response is not successful
|
|
158
|
-
# @example Stream filtered tweets
|
|
159
|
-
# client.stream("tweets/search/stream") { |tweet| puts tweet }
|
|
160
|
-
def stream(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class, &block)
|
|
161
|
-
uri = URI.join(base_url, endpoint)
|
|
162
|
-
request = @request_builder.build(http_method: :get, uri:, headers:, authenticator:)
|
|
163
|
-
@connection.perform_stream(request:) do |response|
|
|
164
|
-
@stream_parser.process(response:, response_parser: @response_parser, array_class:, object_class:, &block)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
private
|
|
169
|
-
|
|
170
|
-
# Initialize default JSON parsing classes
|
|
171
|
-
# @api private
|
|
172
|
-
# @param default_array_class [Class] the default class for parsing JSON arrays
|
|
173
|
-
# @param default_object_class [Class] the default class for parsing JSON objects
|
|
174
|
-
# @return [void]
|
|
175
|
-
def initialize_default_classes(default_array_class:, default_object_class:)
|
|
176
|
-
@default_array_class = default_array_class
|
|
177
|
-
@default_object_class = default_object_class
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Execute an HTTP request to the X API
|
|
181
|
-
# @api private
|
|
182
|
-
# @return [Hash, Array, nil] the parsed response body
|
|
183
|
-
def execute_request(http_method, endpoint, body: nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
|
|
184
|
-
uri = URI.join(base_url, endpoint)
|
|
185
|
-
request = @request_builder.build(http_method:, uri:, body:, headers:, authenticator:)
|
|
186
|
-
response = @connection.perform(request:)
|
|
187
|
-
response = @redirect_handler.handle(response:, request:, base_url:, authenticator:)
|
|
188
|
-
@response_parser.parse(response:, array_class:, object_class:)
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
end
|
data/lib/x/client_credentials.rb
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
module X
|
|
2
|
-
# Mixin for client authentication credentials
|
|
3
|
-
# @api private
|
|
4
|
-
module ClientCredentials
|
|
5
|
-
# The API key for OAuth 1.0a authentication
|
|
6
|
-
# @api public
|
|
7
|
-
# @return [String, nil] the API key for OAuth 1.0a authentication
|
|
8
|
-
# @example Get the API key
|
|
9
|
-
# client.api_key
|
|
10
|
-
attr_reader :api_key
|
|
11
|
-
|
|
12
|
-
# The API key secret for OAuth 1.0a authentication
|
|
13
|
-
# @api public
|
|
14
|
-
# @return [String, nil] the API key secret for OAuth 1.0a authentication
|
|
15
|
-
# @example Get the API key secret
|
|
16
|
-
# client.api_key_secret
|
|
17
|
-
attr_reader :api_key_secret
|
|
18
|
-
|
|
19
|
-
# The access token for OAuth authentication
|
|
20
|
-
# @api public
|
|
21
|
-
# @return [String, nil] the access token for OAuth authentication
|
|
22
|
-
# @example Get the access token
|
|
23
|
-
# client.access_token
|
|
24
|
-
attr_reader :access_token
|
|
25
|
-
|
|
26
|
-
# The access token secret for OAuth 1.0a authentication
|
|
27
|
-
# @api public
|
|
28
|
-
# @return [String, nil] the access token secret for OAuth 1.0a authentication
|
|
29
|
-
# @example Get the access token secret
|
|
30
|
-
# client.access_token_secret
|
|
31
|
-
attr_reader :access_token_secret
|
|
32
|
-
|
|
33
|
-
# The bearer token for authentication
|
|
34
|
-
# @api public
|
|
35
|
-
# @return [String, nil] the bearer token for authentication
|
|
36
|
-
# @example Get the bearer token
|
|
37
|
-
# client.bearer_token
|
|
38
|
-
attr_reader :bearer_token
|
|
39
|
-
|
|
40
|
-
# The OAuth 2.0 client ID
|
|
41
|
-
# @api public
|
|
42
|
-
# @return [String, nil] the OAuth 2.0 client ID
|
|
43
|
-
# @example Get the client ID
|
|
44
|
-
# client.client_id
|
|
45
|
-
attr_reader :client_id
|
|
46
|
-
|
|
47
|
-
# The OAuth 2.0 client secret
|
|
48
|
-
# @api public
|
|
49
|
-
# @return [String, nil] the OAuth 2.0 client secret
|
|
50
|
-
# @example Get the client secret
|
|
51
|
-
# client.client_secret
|
|
52
|
-
attr_reader :client_secret
|
|
53
|
-
|
|
54
|
-
# The OAuth 2.0 refresh token
|
|
55
|
-
# @api public
|
|
56
|
-
# @return [String, nil] the OAuth 2.0 refresh token
|
|
57
|
-
# @example Get the refresh token
|
|
58
|
-
# client.refresh_token
|
|
59
|
-
attr_reader :refresh_token
|
|
60
|
-
|
|
61
|
-
# Set the API key for OAuth 1.0a authentication
|
|
62
|
-
#
|
|
63
|
-
# @api public
|
|
64
|
-
# @param api_key [String] the API key for OAuth 1.0a authentication
|
|
65
|
-
# @return [void]
|
|
66
|
-
# @example Set the API key
|
|
67
|
-
# client.api_key = "new_key"
|
|
68
|
-
def api_key=(api_key)
|
|
69
|
-
@api_key = api_key
|
|
70
|
-
initialize_authenticator
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Set the API key secret for OAuth 1.0a authentication
|
|
74
|
-
#
|
|
75
|
-
# @api public
|
|
76
|
-
# @param api_key_secret [String] the API key secret for OAuth 1.0a authentication
|
|
77
|
-
# @return [void]
|
|
78
|
-
# @example Set the API key secret
|
|
79
|
-
# client.api_key_secret = "new_secret"
|
|
80
|
-
def api_key_secret=(api_key_secret)
|
|
81
|
-
@api_key_secret = api_key_secret
|
|
82
|
-
initialize_authenticator
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Set the access token for OAuth authentication
|
|
86
|
-
#
|
|
87
|
-
# @api public
|
|
88
|
-
# @param access_token [String] the access token for OAuth authentication
|
|
89
|
-
# @return [void]
|
|
90
|
-
# @example Set the access token
|
|
91
|
-
# client.access_token = "new_token"
|
|
92
|
-
def access_token=(access_token)
|
|
93
|
-
@access_token = access_token
|
|
94
|
-
initialize_authenticator
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Set the access token secret for OAuth 1.0a authentication
|
|
98
|
-
#
|
|
99
|
-
# @api public
|
|
100
|
-
# @param access_token_secret [String] the access token secret for OAuth 1.0a authentication
|
|
101
|
-
# @return [void]
|
|
102
|
-
# @example Set the access token secret
|
|
103
|
-
# client.access_token_secret = "new_secret"
|
|
104
|
-
def access_token_secret=(access_token_secret)
|
|
105
|
-
@access_token_secret = access_token_secret
|
|
106
|
-
initialize_authenticator
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Set the bearer token for authentication
|
|
110
|
-
#
|
|
111
|
-
# @api public
|
|
112
|
-
# @param bearer_token [String] the bearer token for authentication
|
|
113
|
-
# @return [void]
|
|
114
|
-
# @example Set the bearer token
|
|
115
|
-
# client.bearer_token = "new_token"
|
|
116
|
-
def bearer_token=(bearer_token)
|
|
117
|
-
@bearer_token = bearer_token
|
|
118
|
-
initialize_authenticator
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Set the OAuth 2.0 client ID
|
|
122
|
-
#
|
|
123
|
-
# @api public
|
|
124
|
-
# @param client_id [String] the OAuth 2.0 client ID
|
|
125
|
-
# @return [void]
|
|
126
|
-
# @example Set the client ID
|
|
127
|
-
# client.client_id = "new_id"
|
|
128
|
-
def client_id=(client_id)
|
|
129
|
-
@client_id = client_id
|
|
130
|
-
initialize_authenticator
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Set the OAuth 2.0 client secret
|
|
134
|
-
#
|
|
135
|
-
# @api public
|
|
136
|
-
# @param client_secret [String] the OAuth 2.0 client secret
|
|
137
|
-
# @return [void]
|
|
138
|
-
# @example Set the client secret
|
|
139
|
-
# client.client_secret = "new_secret"
|
|
140
|
-
def client_secret=(client_secret)
|
|
141
|
-
@client_secret = client_secret
|
|
142
|
-
initialize_authenticator
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Set the OAuth 2.0 refresh token
|
|
146
|
-
#
|
|
147
|
-
# @api public
|
|
148
|
-
# @param refresh_token [String] the OAuth 2.0 refresh token
|
|
149
|
-
# @return [void]
|
|
150
|
-
# @example Set the refresh token
|
|
151
|
-
# client.refresh_token = "new_token"
|
|
152
|
-
def refresh_token=(refresh_token)
|
|
153
|
-
@refresh_token = refresh_token
|
|
154
|
-
initialize_authenticator
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
private
|
|
158
|
-
|
|
159
|
-
# Initialize credential instance variables
|
|
160
|
-
# @api private
|
|
161
|
-
# @return [void]
|
|
162
|
-
def initialize_credentials(api_key:, api_key_secret:, access_token:, access_token_secret:, bearer_token:,
|
|
163
|
-
client_id:, client_secret:, refresh_token:)
|
|
164
|
-
@api_key = api_key
|
|
165
|
-
@api_key_secret = api_key_secret
|
|
166
|
-
@access_token = access_token
|
|
167
|
-
@access_token_secret = access_token_secret
|
|
168
|
-
@bearer_token = bearer_token
|
|
169
|
-
@client_id = client_id
|
|
170
|
-
@client_secret = client_secret
|
|
171
|
-
@refresh_token = refresh_token
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
# Initialize the appropriate authenticator based on available credentials
|
|
175
|
-
# @api private
|
|
176
|
-
# @return [Authenticator] the initialized authenticator
|
|
177
|
-
def initialize_authenticator
|
|
178
|
-
@authenticator = oauth_authenticator || oauth2_authenticator || bearer_authenticator || @authenticator || Authenticator.new
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# Build an OAuth 1.0a authenticator if credentials are available
|
|
182
|
-
# @api private
|
|
183
|
-
# @return [OAuthAuthenticator, nil] the OAuth authenticator or nil
|
|
184
|
-
def oauth_authenticator
|
|
185
|
-
return unless api_key && api_key_secret && access_token && access_token_secret
|
|
186
|
-
|
|
187
|
-
OAuthAuthenticator.new(api_key:, api_key_secret:, access_token:, access_token_secret:)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# Build an OAuth 2.0 authenticator if credentials are available
|
|
191
|
-
# @api private
|
|
192
|
-
# @return [OAuth2Authenticator, nil] the OAuth 2.0 authenticator or nil
|
|
193
|
-
def oauth2_authenticator
|
|
194
|
-
return unless client_id && client_secret && access_token && refresh_token
|
|
195
|
-
|
|
196
|
-
OAuth2Authenticator.new(client_id:, client_secret:, access_token:, refresh_token:)
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# Build a bearer token authenticator if credentials are available
|
|
200
|
-
# @api private
|
|
201
|
-
# @return [BearerTokenAuthenticator, nil] the bearer token authenticator or nil
|
|
202
|
-
def bearer_authenticator
|
|
203
|
-
return unless bearer_token
|
|
204
|
-
|
|
205
|
-
BearerTokenAuthenticator.new(bearer_token:)
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
end
|