Skip to content

Commit

Permalink
0.9.7 (#161)
Browse files Browse the repository at this point in the history
* update VSCode remote container settings

Use docker-compose and mount volume between host and container properly

* "-v" option to include Ruby runtime version

* Support of Ruby 2.4 has ended

https://www.ruby-lang.org/en/news/2020/04/05/support-of-ruby-2-4-has-ended/

* #157 fix parse rule and better error message

* remove --no-ssl option

* do not allow alias to use reserved command names

* max_retries is available since Ruby 2.5

and now we require spec.required_ruby_version = '>= 2.5.0' so no need to have this condition anymore

* don't exit

* --raw-data option is fake

Although the description says "Sends the specified data as it is..." it's actually parsing input data using CGI.parse() method. This isn't ideal, especially when sending JSON data, for instance. This commit changes this behavior and makes that option a real thing, so users can also send JSON data using -r option by setting the content-type request header using -A option.

For backword-compatibility, we still allow -d option to send JSON data as long as "content-type: application/json" request header is set.

Also, since -r takes input data as-is, it doesn't really make sense to allow combine use with -d option or adding -r option more than once, so we treat them as an error going forward.

* error message should use Exception over CLI.puts

* workaround for OpenStruct bug in Ruby 3.0.0

This commit can be reverted once new Ruby 3 patch/minor version has released.

* update .travis.yml

* remove coveralls

* Revert "workaround for OpenStruct bug in Ruby 3.0.0"

see #173

* Update devcontainer.json

* prevent panic message on Ctrl-C in PIN auth flow
  • Loading branch information
smaeda-ks committed Apr 26, 2023
1 parent 3604e65 commit 3d8a490
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 125 deletions.
12 changes: 8 additions & 4 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{
"name": "twurl dev container",
"dockerFile": "Dockerfile",
"context": "..",
"dockerComposeFile": "./docker-compose.yml",
"service": "twurl",
"workspaceFolder": "/usr/src/app",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
"terminal.integrated.profiles.linux": {
"bash": {
"path": "bash",
"args": ["-l"]
}
}
},
"shutdownAction": "none",
"extensions": [
"rebornix.Ruby"
]
Expand Down
9 changes: 9 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
version: "3"
services:
twurl:
build:
context: ..
dockerfile: ./.devcontainer/Dockerfile
command: /bin/sh -c "while sleep 1000; do :; done"
volumes:
- ../:/usr/src/app
8 changes: 6 additions & 2 deletions lib/twurl/aliases_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ def dispatch
end
when 1
if options.path
OAuthClient.rcfile.alias(options.subcommands.first, options.path)
if Twurl::CLI::SUPPORTED_COMMANDS.include?(options.subcommands.first)
raise Exception, "ERROR: '#{options.subcommands.first}' is reserved for commands. Please use different alias name."
else
OAuthClient.rcfile.alias(options.subcommands.first, options.path)
end
else
CLI.puts NO_PATH_PROVIDED_MESSAGE
raise Exception, NO_PATH_PROVIDED_MESSAGE
end
end
end
Expand Down
9 changes: 3 additions & 6 deletions lib/twurl/app_only_oauth_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,9 @@ def set_http_client_options(http)
http.set_debug_output(Twurl.options.debug_output_io) if Twurl.options.trace
http.read_timeout = http.open_timeout = Twurl.options.timeout || 60
http.open_timeout = Twurl.options.connection_timeout if Twurl.options.connection_timeout
# Only override if Net::HTTP support max_retries (since Ruby >= 2.5)
http.max_retries = 0 if http.respond_to?(:max_retries=)
if Twurl.options.ssl?
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
http.max_retries = 0
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http
end

Expand Down
49 changes: 20 additions & 29 deletions lib/twurl/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@ class CLI
PROTOCOL_PATTERN = /^\w+:\/\//
README = File.dirname(__FILE__) + '/../../README.md'
@output ||= STDOUT
class NoPathFound < Exception
end

class << self
attr_accessor :output

def run(args)
begin
options = parse_options(args)
rescue NoPathFound => e
exit
rescue Twurl::Exception => exception
abort(exception.message)
end
dispatch(options)
end
Expand Down Expand Up @@ -80,7 +78,6 @@ def parse_options(args)
headers
host
quiet
disable_ssl
request_method
help
version
Expand All @@ -98,19 +95,19 @@ def parse_options(args)
begin
arguments = option_parser.parse!(args)
rescue OptionParser::InvalidOption
CLI.puts "ERROR: undefined option"
exit
raise Exception "ERROR: undefined option"
rescue Twurl::Exception
raise
rescue
CLI.puts "ERROR: invalid argument"
exit
raise Exception "ERROR: invalid argument"
end
Twurl.options.command = extract_command!(arguments)
Twurl.options.path = extract_path!(arguments)
Twurl.options.subcommands = arguments

if Twurl.options.command == DEFAULT_COMMAND and Twurl.options.path.nil? and Twurl.options.args.empty?
CLI.puts option_parser
raise NoPathFound, "No path found"
raise Exception, "No path found"
end

Twurl.options
Expand Down Expand Up @@ -172,7 +169,7 @@ def extract_path!(arguments)

def escape_params(params)
CGI::parse(params).map do |key, value|
"#{CGI.escape key}=#{CGI.escape value.first}"
"#{CGI.escape(key)}=#{CGI.escape(value.first)}"
end.join("&")
end
end
Expand Down Expand Up @@ -234,22 +231,26 @@ def trace

def data
on('-d', '--data [data]', 'Sends the specified data in a POST request to the HTTP server.') do |data|
if options.args.count { |item| /content-type: (.*)/i.match(item) } > 0
options.data[data] = nil
if options.args.count { |item| /^content-type:\s+application\/json/i.match(item) } > 0
options.json_data = true
options.data = data
else
data.split('&').each do |pair|
key, value = pair.split('=', 2)
options.data[key] = value
CGI.parse(data).each_pair do |key, value|
options.data[key] = value.first
end
end
end
end

def raw_data
on('-r', '--raw-data [data]', 'Sends the specified data as it is in a POST request to the HTTP server.') do |data|
CGI::parse(data).each_pair do |key, value|
options.data[key] = value.first
if options.raw_data
raise Exception, "ERROR: can't specify '-r' option more than once"
elsif options.args.include?('-d') || options.args.include?('--data')
raise Exception, "ERROR: can't use '-r' and '-d' options together"
end
options.raw_data = true
options.data = data
end
end

Expand Down Expand Up @@ -277,12 +278,6 @@ def quiet
end
end

def disable_ssl
on('-U', '--no-ssl', 'Disable SSL (default: SSL is enabled)') do |use_ssl|
options.protocol = 'http'
end
end

def request_method
on('-X', '--request-method [method]', 'Request method (default: GET)') do |request_method|
options.request_method = request_method.downcase
Expand All @@ -298,7 +293,7 @@ def help

def version
on_tail("-v", "--version", "Show version") do
CLI.puts Version
CLI.puts "twurl version: #{Version}\nplatform: #{RUBY_ENGINE} #{RUBY_VERSION} (#{RUBY_PLATFORM})"
exit
end
end
Expand Down Expand Up @@ -374,10 +369,6 @@ def base_url
"#{protocol}://#{host}"
end

def ssl?
protocol == 'https'
end

def debug_output_io
super || STDERR
end
Expand Down
37 changes: 21 additions & 16 deletions lib/twurl/oauth_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,20 @@ def build_request_from_options(options, &block)

request.body = multipart_body.join
request.content_type = "multipart/form-data, boundary=\"#{boundary}\""
elsif request.content_type && options.data
request.body = options.data.keys.first
elsif options.json_data
request.body = options.data
elsif options.data
request.content_type = "application/x-www-form-urlencoded"
if options.data.length == 1 && options.data.values.first == nil
request.body = options.data.keys.first
request.content_type = "application/x-www-form-urlencoded" unless request.content_type
if options.raw_data
request.body = options.data
else
request.body = options.data.map do |key, value|
"#{key}=#{CGI.escape value}"
end.join("&")
begin
request.body = options.data.map do |key, value|
"#{key}" + (value.nil? ? "" : "=#{CGI.escape(value)}")
end.join("&")
rescue
raise Exception, "ERROR: failed to parse POST request body"
end
end
end
request
Expand Down Expand Up @@ -202,7 +206,11 @@ def exchange_credentials_for_access_token
def perform_pin_authorize_workflow
@request_token = consumer.get_request_token
CLI.puts("Go to #{generate_authorize_url} and paste in the supplied PIN")
pin = STDIN.gets
begin
pin = STDIN.gets.chomp
rescue SystemExit, Interrupt
raise Exception, "Operation cancelled"
end
access_token = @request_token.get_access_token(:oauth_verifier => pin.chomp)
{:oauth_token => access_token.token, :oauth_token_secret => access_token.secret}
end
Expand All @@ -212,7 +220,7 @@ def generate_authorize_url
params = request['Authorization'].sub(/^OAuth\s+/, '').split(/,\s+/).map { |p|
k, v = p.split('=')
v =~ /"(.*?)"/
"#{k}=#{CGI::escape($1)}"
"#{k}=#{CGI.escape($1)}"
}.join('&')
"#{Twurl.options.base_url}#{request.path}?#{params}"
end
Expand Down Expand Up @@ -260,12 +268,9 @@ def configure_http!
consumer.http.set_debug_output(Twurl.options.debug_output_io) if Twurl.options.trace
consumer.http.read_timeout = consumer.http.open_timeout = Twurl.options.timeout || 60
consumer.http.open_timeout = Twurl.options.connection_timeout if Twurl.options.connection_timeout
# Only override if Net::HTTP support max_retries (since Ruby >= 2.5)
consumer.http.max_retries = 0 if consumer.http.respond_to?(:max_retries=)
if Twurl.options.ssl?
consumer.http.use_ssl = true
consumer.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
consumer.http.max_retries = 0
consumer.http.use_ssl = true
consumer.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end

def consumer
Expand Down
6 changes: 3 additions & 3 deletions lib/twurl/request_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ def perform_request
}
}
rescue URI::InvalidURIError
CLI.puts INVALID_URI_MESSAGE
raise Exception, INVALID_URI_MESSAGE
rescue Net::ReadTimeout
CLI.puts READ_TIMEOUT_MESSAGE
raise Exception, READ_TIMEOUT_MESSAGE
rescue Net::OpenTimeout
CLI.puts OPEN_TIMEOUT_MESSAGE
raise Exception, OPEN_TIMEOUT_MESSAGE
end
end

Expand Down
10 changes: 6 additions & 4 deletions test/alias_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,15 @@ def test_when_alias_and_value_are_provided_they_are_added
controller.dispatch
end

def test_when_no_path_is_provided_nothing_happens
def test_error_if_no_path_is_provided
options.subcommands = ['a']
assert_nil options.path

mock(Twurl::CLI).puts(Twurl::AliasesController::NO_PATH_PROVIDED_MESSAGE).times(1)
e = assert_raises Twurl::Exception do
controller = Twurl::AliasesController.new(client, options)
controller.dispatch
end

controller = Twurl::AliasesController.new(client, options)
controller.dispatch
assert_equal Twurl::AliasesController::NO_PATH_PROVIDED_MESSAGE, e.message
end
end
13 changes: 2 additions & 11 deletions test/cli_options_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,8 @@ def setup
end

def test_base_url_is_built_from_protocol_and_host
options.protocol = 'http'
options.host = 'api.twitter.com'
options = Twurl::CLI.parse_options(['-H', 'ads-api.twitter.com'])

assert_equal 'http://api.twitter.com', options.base_url
end

def test_ssl_is_enabled_if_the_protocol_is_https
options.protocol = 'http'
assert !options.ssl?

options.protocol = 'https'
assert options.ssl?
assert_equal 'https://ads-api.twitter.com', options.base_url
end
end

0 comments on commit 3d8a490

Please sign in to comment.