Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Faraday to >= 2.0 #406

Merged
merged 5 commits into from
May 7, 2022
Merged

Conversation

schinery
Copy link
Contributor

@schinery schinery commented May 3, 2022

First attempt at updating Faraday to use >= 2.0, which for the most part was fairly straightforward with the following exceptions:

1: Updating lib/slack/web/faraday/response/raise_error.rb

The spec in spec/slack/web/client_spec.rb:281 was failing with the following error:

1) Slack::Web::Client global config server failures parsing error raises ParsingError
     Failure/Error: expect { request }.to raise_error(Slack::Web::Api::Errors::ParsingError).with_message('parsing_error')

       expected Slack::Web::Api::Errors::ParsingError with "parsing_error", got #<NoMethodError: undefined method `map' for nil:NilClass

                     body['error'] || body['errors'].map { |message| message['error'] }.join(',')
                                                    ^^^^> with backtrace:
         # ./lib/slack/web/faraday/response/raise_error.rb:18:in `on_complete'
         # ./lib/slack/web/faraday/response/raise_error.rb:26:in `call'
         # ./lib/slack/web/faraday/request.rb:25:in `request'
         # ./lib/slack/web/faraday/request.rb:11:in `post'
         # ./lib/slack/web/api/endpoints/api.rb:17:in `api_test'
         # ./spec/slack/web/client_spec.rb:269:in `block (4 levels) in <top (required)>'
         # ./spec/slack/web/client_spec.rb:282:in `block (6 levels) in <top (required)>'
         # ./spec/slack/web/client_spec.rb:282:in `block (5 levels) in <top (required)>'
     # ./spec/slack/web/client_spec.rb:282:in `block (5 levels) in <top (required)>'

Finished in 10.54 seconds (files took 1.93 seconds to load)
461 examples, 1 failure, 8 pending

Failed examples:

rspec ./spec/slack/web/client_spec.rb:281 # Slack::Web::Client global config server failures parsing error raises ParsingError

I believe this is caused by there being no middleware equivalent to ::FaradayMiddleware::ParseJson, and the new :json request/response middleware appears to only parse (and throw ::Faraday::ParsingError) if the content type is application/json.

So, I've added some code to check if the env.body has been converted into Hash or not, and attempt to convert if it isn't.

It does raise the question does ::Faraday::ParsingError need to rescued somewhere else?

2: Not being able to use slack-ruby-danger

The danger gem currently doesn't support Faraday 2.0 so for the moment I've commented it out. It has been mentioned about pulling it into its own Gemfile.

@schinery schinery force-pushed the update-faraday branch 2 times, most recently from 80e18b9 to cb80a2e Compare May 3, 2022 05:50
@schinery
Copy link
Contributor Author

schinery commented May 3, 2022

Worth noting, I tried running the specs with the CONCURRENCY and SLACK_API_TOKEN set but was having issues with the Slack token. The rest of the specs ran successfully, so fingers crossed these will run ok with the correct setup.

@schinery schinery marked this pull request as ready for review May 3, 2022 06:16
@dblock
Copy link
Collaborator

dblock commented May 3, 2022

I believe this is caused by there being no middleware equivalent to ::FaradayMiddleware::ParseJson, and the new :json request/response middleware appears to only parse (and throw ::Faraday::ParsingError) if the content type is application/json.

That makes a lot of sense. Change the spec to supply application/json.

So, I've added some code to check if the env.body has been converted into Hash or not, and attempt to convert if it isn't.

It's probably good to be defensive and not reach into the hash unless it's a hash, but don't attempt to convert it. Add a separate spec with the correct content-type and with a different one.

It does raise the question does ::Faraday::ParsingError need to rescued somewhere else?

Probably yes, and raise Slack::Web::Api::Errors::ParsingError that would wrap that one?

2: Not being able to use slack-ruby-danger

The danger gem currently doesn't support Faraday 2.0 so for the moment I've commented it out. It has been mentioned about pulling it into its own Gemfile.

Yep, I did it in https://github.com/slack-ruby/slack-ruby-bot-server/pull/142/files#diff-9ed6d3c8f52cf7b6862dd9628741eda3dbcd3683b1485977c483b6571e989904 if you just want to copy-paste.

Copy link
Collaborator

@dblock dblock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good progress. More comments below.

Also update CHANGELOG, README, and remove incompatible versions of Ruby from CI.

@@ -15,13 +15,15 @@ end
group :test do
gem 'activesupport'
gem 'erubis'
gem 'faraday-typhoeus'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this becoming a gem dependency? Does it belong in .gemspec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added as testing dependency because it was required for the 'non default adapter' spec in spec/slack/web/client_spec.rb:177 and the adapters are no longer bundled in.

error_class = Slack::Web::Api::Errors::ERROR_CLASSES[error_message]
error_class ||= Slack::Web::Api::Errors::SlackError
raise error_class.new(error_message, env.response)
# TODO: Do we need to still handle ::Faraday::ParsingError or will this be handled else where?
# rescue ::Faraday::ParsingError
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rescue it the same way as we rescued JSON::ParseError before, update Slack::Web::Api::Errors::ParsingError to also include the inner exception

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parse rescue has been moved to lib/slack/web/faraday/request.rb as the Faraday::ParsingError is now triggered when processing the response.

As part of this, and to handle non JSON responses such as HTML 500s, I've currently set content_type: /\b*$/ in the :json response options. The question is do you think this should be more restrictive and only parse certain known content types?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means content-type can be anything, doesn't it? So what's the point in having it? I think we should just try to parse any response because the Slack API says it will always send JSON, except when it's broken, and that's when we need this.

# Need to check if the body has been parsed here as it will only be parsed by the Faraday JSON middleware if
# the response is content_type 'application/json'. This would of been handled automatically by the
# ::FaradayMiddleware::ParseJson but that is no longer available.
def process_body(env)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like this is no longer needed because Faraday does it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this wasn't needed after all and has been removed.

@@ -280,8 +281,7 @@
it 'raises ParsingError' do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modify to include the correct content-type.

Write a test without the correct content-type, too, possibly changing expectations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the spec that checked for the HTML response and added one for malformed JSON.

There is the question above regarding correct content types as the new code will currently try and parse any content type returned, which I guess these two specs cover?

@schinery
Copy link
Contributor Author

schinery commented May 3, 2022

@dblock I'll look at updating based on your above comments when I get a moment 👍🏻

Another thing, I see that the linting failed because newly added faraday-typhoeus gem only supports Ruby >= 2.7.0. As Ruby 2.5 has reached it's EOL and Ruby 2.6 does this month, should the new version of this Slack client be >= 2.7?

@schinery
Copy link
Contributor Author

schinery commented May 3, 2022

Also update CHANGELOG, README, and remove incompatible versions of Ruby from CI.

Ah this answers my Ruby version question above 👍🏻

@@ -3,7 +3,9 @@
* [#400](https://github.com/slack-ruby/slack-ruby-client/pull/400): Replace Travis-CI with GitHub Actions - [@dblock](https://github.com/dblock).
* [#401](https://github.com/slack-ruby/slack-ruby-client/pull/401): Upgraded RuboCop to 1.26.0 - [@dblock](https://github.com/dblock).
* [#401](https://github.com/slack-ruby/slack-ruby-client/pull/401): Added support for Ruby 3.1.1 - [@dblock](https://github.com/dblock).
* Your contribution here.
* [#406](https://github.com/slack-ruby/slack-ruby-client/pull/406): Update Faraday to `>= 2.0` - [@schinery](https://github.com/slack-ruby/slack-ruby-client/pull/406).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's bump the version to 1.1 (version.rb, etc.)

@schinery schinery force-pushed the update-faraday branch 11 times, most recently from 27783da to 25bf1dd Compare May 4, 2022 08:08
@schinery
Copy link
Contributor Author

schinery commented May 4, 2022

@dblock I think this is ready to be re-reviewed as I've...

  • Resolved the JSON parsing stuff previously discussed. There is this question which based on the answer might need some additional changes
  • Created Gemfile.danger and set the GitHub action to use that so Faraday 1.0 doesn't affect the main bundle install
  • Updated the CHANGELOG, although let me know if I've gone a bit overboard here

One thing, I couldn't see what might need updating in the README except maybe updating the Stable Release section. Let me know if this is what you meant.

@schinery schinery requested a review from dblock May 4, 2022 08:27
Copy link
Collaborator

@dblock dblock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're almost there! Made a bunch of minor comments and try to address the content-type one below.

- { ruby: 2.7.1, concurrency: async-websocket }
- { ruby: 3.0.2 }
- { ruby: 3.1.1 }
- { ruby: 2.7 }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: quote all rubies so we don't make the 3.0 being a float mistake

CHANGELOG.md Outdated
* [#406](https://github.com/slack-ruby/slack-ruby-client/pull/406): Upgraded RuboCop to 1.26.1 and lock to version - [@schinery](https://github.com/slack-ruby/slack-ruby-client/pull/406).
* [#406](https://github.com/slack-ruby/slack-ruby-client/pull/406): Updated Danger PR Linter GitHub action to use separate `Gemfile` - [@schinery](https://github.com/slack-ruby/slack-ruby-client/pull/406).
* Your contribution here.

### 1.0.1 (Next)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hasn't shipped, merge with the above.

### 1.0.1 (Next)

* [#400](https://github.com/slack-ruby/slack-ruby-client/pull/400): Replace Travis-CI with GitHub Actions - [@dblock](https://github.com/dblock).
* [#401](https://github.com/slack-ruby/slack-ruby-client/pull/401): Upgraded RuboCop to 1.26.0 - [@dblock](https://github.com/dblock).
* [#401](https://github.com/slack-ruby/slack-ruby-client/pull/401): Added support for Ruby 3.1.1 - [@dblock](https://github.com/dblock).
* [#405](https://github.com/slack-ruby/slack-ruby-client/pull/405): Added `admin_apps_requests_cancel`, `admin_users_unsupportedVersions_export`, `bookmarks_add`, `bookmarks_edit`, `bookmarks_list`, `bookmarks_remove` - [@dblock](https://github.com/dblock).
* [#404](https://github.com/slack-ruby/slack-ruby-client/pull/404): Update `chat_update` method not to raise error with parameter which contains only `reply_broadcast` - [@colorbox](https://github.com/colorbox).
* Your contribution here.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put this one back.

# Match any content type for JSON responses meaning Faraday::ParsingError will be raised for non-JSON
# responses such as HTML 500 errors.
# TODO: Should this be more restrictive?
connection.response :json, content_type: /\b*$/
Copy link
Collaborator

@dblock dblock May 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is any content type, isn't it? Do any specs fail if you remove content_type here? I think not, and it's the old behavior that basically says "try to parse anything, which now raises ::Faraday::ParsingError, which we handle properly".

Remove comment and TODO above.

Copy link
Contributor Author

@schinery schinery May 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is any content type, isn't it? Do any specs fail if you remove content_type here? I think not, and it's the old behavior that basically says "try to parse anything, which now raises ::Faraday::ParsingError, which we handle properly".

If we remove this content_type: option then the <html></html> response specs will fail because the default for the JSON middleware parsing is /\bjson$/, so we wouldn't attempt to parse if a 500 HTML response was returned and we end up with the undefined method 'map' for nil:NilClass mention in the initial PR description.

I guess there are two options:

  1. Leave as is it now meaning that it will try and parse any response Slack returns, which I think as you say is the old behaviour handled by the deprecated ::FaradayMiddleware::ParseJson middleware
  2. Make the content types stricter and set it to only attempt to parse JSON or HTML content types (or whatever we know is returned) as we can set an array of content types

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove comment and TODO above.

Removed 👍🏻

@schinery schinery requested a review from dblock May 5, 2022 06:30
Copy link
Collaborator

@dblock dblock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! Merging. Let's see what falls out in integration tests run on master with a token. My local run with CONCURRENCY=async-websocket SLACK_API_TOKEN=... bundle exec rake runs integration tests successfully.

@dblock dblock merged commit bfd493e into slack-ruby:master May 7, 2022
@dblock
Copy link
Collaborator

dblock commented May 7, 2022

Btw, if you are bored, I have a few more clients that could use some love of upgrading faraday, https://github.com/dblock/strava-ruby-client, https://github.com/dblock/iex-ruby-client, and https://github.com/dblock/open-weather-ruby-client.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants