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

Validate user info block configuration #52

Merged
merged 1 commit into from
Aug 20, 2020

Conversation

thestrabusiness
Copy link
Contributor

@thestrabusiness thestrabusiness commented Aug 19, 2020

🧰 What's being changed?

This commit adds some validation around user info block required by the
middleware.

One check runs at application boot and ensures that the block is not
nil.

The other checks occur at run time and ensure that if the block is not
configured properly, the application will not crash and log an error
instead. The request to the Readme API will be aborted and the response
will be passed on through the rest of the middleware stack.

🧪 Testing

The new checks are covered by the automated test suite.

Without a block configured:

$LOAD_PATH << File.expand_path("../../metrics-sdks/packages/ruby/lib", __FILE__)
require "readme/metrics"

class ApiApp
  def call(env)
    [200, {"Content-Length" => "2"}, ["Ok"]]
  end
end

map "/api" do
  options = {api_key: "API_KEY"}
  use Readme::Metrics, options
  run ApiApp.new
end
/Users/anthony/Projects/metrics-sdks/packages/ruby/lib/readme/metrics.rb:18:in `initialize': Missing block argument. You must provide a block when configuring the (Readme::Errors::ConfigurationError)
middleware. The block must return a hash containing user info:
  use Readme::Metrics, options do |env|
    { id: "unique_id", label: "Your user label", email: "Your user email" }
  end

With an improperly configured block (missing label value)

$LOAD_PATH << File.expand_path("../../metrics-sdks/packages/ruby/lib", __FILE__)
require "readme/metrics"

class ApiApp
  def call(env)
    [200, {"Content-Length" => "2"}, ["Ok"]]
  end
end

map "/api" do
  options = {api_key: "api_key"}
  use Readme::Metrics, options do |env|
    {id: "anthonym", email: "[email protected]"}
  end
  run ApiApp.new
end

Running a request produces the following log and the request to the client returns a 200:
Screen Shot 2020-08-19 at 4 15 39 PM

@JoelQ
Copy link
Contributor

JoelQ commented Aug 19, 2020

Can you boot an incorrectly configured application and show a screenshot of the expected errors in the "Testing" section of the PR description?

@thestrabusiness thestrabusiness requested a review from JoelQ August 19, 2020 20:16
BUFFER_LENGTH_ERROR = "buffer_length must be an Integer"
DEVELOPMENT_ERROR = "development option must be a boolean"

MISSING_BLOCK_ERROR = <<~MESSAGE
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice use of the squiggly heredoc!

Comment on lines 4 to 7
REJECT_PARAMS_ERROR = "reject_params option must be an array of strings"
ALLOW_ONLY_ERROR = "allow_only option must be an array of strings"
BUFFER_LENGTH_ERROR = "buffer_length must be an Integer"
DEVELOPMENT_ERROR = "development option must be a boolean"
Copy link
Contributor

Choose a reason for hiding this comment

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

Thoughts on capitalizing the first letter of these errors? Or are they being inserted into an existing sentence?

Copy link
Member

Choose a reason for hiding this comment

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

Thoughts on wrapping the options in backticks? Like:

`reject_params` option must be an array of strings

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestions. I made a couple of changes.

Comment on lines 17 to 18
class BadBlockMessage
def self.call(result)
Copy link
Contributor

Choose a reason for hiding this comment

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

Thoughts on making this a class method Readme::Errors.bad_block_message rather than a callable constant? I think that might be a bit more idiomatic.

Comment on lines 41 to 49
begin
user_info = @get_user_info.call(env)
raise if user_info_invalid?(user_info)
rescue
puts Errors::BadBlockMessage.call(user_info)
return [status, headers, body]
end
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure we need a begin/end block here if we're manually raising? This feels like using exceptions for control-flow. Thoughts on expressing this as a conditional instead?

user_info = @get_user_info.call(env)

if user_info_invalid?(user_info)
  puts Errors::BadBlockMessage.call(user_info)
else
  payload = Payload.new(har, user_info, development: @development)
  @@request_queue.push(payload.to_json)
end

[status, headers, body]

I could see extracting the conditional into a private method so call doesn't get too cluttered

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Part of what I wanted to do here was catch the situation where the block itself raises an exception so the application can continue running. Does that make sense to do?

Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you're saying. I kind of feels like feature creep though? We have a separate card for not crashing the client request if the middleware has an issue. I think we'll want a single rescue around the whole process.

If the block raises, then we can't send a valid payload to Readme so I don't think there is much value to adding a local rescue just for the block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great that makes sense. I'll update this to not use rescue block.

Comment on lines 2 to 7
module Errors
API_KEY_ERROR = "Missing API Key"
REJECT_PARAMS_ERROR = "reject_params option must be an array of strings"
ALLOW_ONLY_ERROR = "allow_only option must be an array of strings"
BUFFER_LENGTH_ERROR = "buffer_length must be an Integer"
DEVELOPMENT_ERROR = "development option must be a boolean"
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 for making these constants!

@thestrabusiness thestrabusiness force-pushed the am-handle-bad-user-info-block branch from f5fda7a to 4f52fd2 Compare August 20, 2020 13:36
This commit adds some validation around user info block required by the
middleware.

One check runs at application boot and ensures that the block is not
nil.

The other checks occur at run time and ensure that if the block is not
configured properly, the application will not crash and log an error
instead. The request to the Readme API will be aborted and the response
will be passed on through the rest of the middleware stack.
@thestrabusiness thestrabusiness force-pushed the am-handle-bad-user-info-block branch from 4f52fd2 to 52bafff Compare August 20, 2020 13:39
@thestrabusiness thestrabusiness merged commit 52bafff into master Aug 20, 2020
@thestrabusiness thestrabusiness deleted the am-handle-bad-user-info-block branch August 20, 2020 13:39
djmango pushed a commit to djmango/metrics-sdks that referenced this pull request Sep 19, 2023
@erunion erunion added ruby Issues related to our Ruby SDK and removed area:ruby labels Jul 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ruby Issues related to our Ruby SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants