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

Api V1 percentage_of_actors gate endpoint #179

Merged
merged 1 commit into from
Oct 24, 2016

Conversation

AlexWheeler
Copy link
Collaborator

@AlexWheeler AlexWheeler commented Oct 22, 2016

  • POST / DELETE request to enable / disable
  • *send parameter 'percentage' to request percentage to enable
  • only POST requires a percentage parameter to be passed with request
  • percentage_invalid raised and returned if percentage param is missing or not <= 100. I like the name because it keeps with the convention:

:{ thing } _ { error_description }
i.e.
:{feature} _ {not_found}
:{group} _ {not_registered}
:{percentage } _ {invalid}

  • uses regex to make sure percentage parameter is a number <= 100. This is powerful because it handles all the edge cases, and we can keep it in the ensure_valid_post_params method
  1. parameter is missing
  2. parameter is empty string or nil
  3. parameter is out of range
  • adds specs

@AlexWheeler AlexWheeler force-pushed the api-v1-percentage-of-actors-gate branch from f18c2f9 to 9431bd0 Compare October 22, 2016 15:09
end

def percentage
@percentage ||= params.fetch('percentage') { json_error_response(:percentage_invalid) }.to_i
Copy link
Collaborator Author

@AlexWheeler AlexWheeler Oct 22, 2016

Choose a reason for hiding this comment

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

here we use fetch because its possible the client misspelled or did not send the percentage parameter and we'd end up with nil.to_i and we'll end up with 0 instead of responding with error so they can correct the request.

end

def percentage
@percentage ||= params.fetch('percentage') { json_error_response(:percentage_invalid) }.to_i
Copy link
Collaborator Author

@AlexWheeler AlexWheeler Oct 22, 2016

Choose a reason for hiding this comment

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

perhaps we want to pass the percentage into a regex to make sure it is an integer?

since

"percentage": "alex" would also end up with 0

def percentage
  if number_between_0_and_100?(params['percentage'])
    @percentage ||= params['percentage'].to_i
  else
      json_error_response(:percentage_invalid) 
  end
end

def number_between_0_and_100?(val)
  !!/^(100|[1-9][0-9]?)$/.match(val)
end

@AlexWheeler AlexWheeler force-pushed the api-v1-percentage-of-actors-gate branch 2 times, most recently from 52581a1 to 58e70e4 Compare October 22, 2016 15:24
Copy link
Collaborator

@jnunemaker jnunemaker 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 this is ok as is, but did leave a few thoughts on improvements/tweaks. If you like the suggestions, feel free to tweak and merge. If you don't, feel free to merge. Thanks again! This is really coming together nicely.

end

def number_less_than_or_equal_100?(val)
!!/^(100|[1-9][0-9]?)$/.match(val)
Copy link
Collaborator

Choose a reason for hiding this comment

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

^ and $ only match beginning of line. What do you think about using \A and \Z to match beginning and end of string?

Alternatively, did you consider using the stricter integer conversion ruby has (Integer(val))? I've never used a regex for this, so I am just curious what the difference would be between Integer(val) with a range check and a rescue for ArgumentError vs the regex.

irb(main):002:0> Integer("1")
=> 1
irb(main):004:0> Integer("asdf")
ArgumentError: invalid value for Integer(): "asdf"
    from (irb):4:in `Integer'
    from (irb):4
    from /Users/jnunemaker/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

Then, once converted to an integer, I check the if the value is in a valid range. What do you think

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ah I do like this approach better, this way we can always just use percentage instead of params['percentage'] then percentage elsewhere and it splits up the logic nicely

describe 'invalid percentage parameter' do
before do
flipper[:my_feature].disable
post '/api/v1/features/my_feature/percentage_of_actors', { percentage: '300' }
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is a good out of range test. Does it feel worthwhile to add an invalid value as well, like "asdf"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

totally, adding one now thanks

end

def number_less_than_or_equal_100?(val)
!!/^(100|[1-9][0-9]?)$/.match(val)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Technically you can enable for 0 percent using flipper and the flipper-ui. Do you feel we should support the same semantics for the API or just point people to disable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

correct, I think it makes sense to support same semantics especially if users are used to that

* POST / DELETE request to enable / disable
* send parameter 'percentage' to request percentage to enable
* adds specs
@AlexWheeler AlexWheeler force-pushed the api-v1-percentage-of-actors-gate branch from 58e70e4 to 2dadf41 Compare October 24, 2016 13:11
def percentage
@percentage ||= begin
Integer(params['percentage'])
rescue ArgumentError, TypeError
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I like this much better great suggestion. Here we rescue TypeError as well for nil cases Integer(nil)

Copy link
Collaborator

Choose a reason for hiding this comment

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

🆒

it '400s with correct error response when percentage parameter is invalid' do
expect(last_response.status).to eq(400)
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
end
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added test for invalid value that is not an integer or nil

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good stuff!

@AlexWheeler AlexWheeler merged commit 43cc964 into master Oct 24, 2016
@AlexWheeler AlexWheeler deleted the api-v1-percentage-of-actors-gate branch October 24, 2016 13:42
@@ -23,6 +23,7 @@ def as_json
ERRORS = {
feature_not_found: Error.new(1, "Feature not found.", "", 404),
group_not_registered: Error.new(2, "Group not registered.", "", 404),
percentage_invalid: Error.new(3, "Percentage must be a positive number less than or equal to 100.", "", 400),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missed this in my review. Should this be 422? 400 is bad request and that isn't accurate as if it gets here it was a well formed request, it just wasn't valid. I think 422 is what rails typically uses for this type of error. Thoughts?


def ensure_valid_enable_params
json_error_response(:feature_not_found) unless feature_names.include?(feature_name)
json_error_response(:percentage_invalid) unless percentage >= 0 && percentage <= 100
Copy link
Collaborator

Choose a reason for hiding this comment

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

I typically try to avoid unless with more than one conditional. I would probably have written this if percentage < 0 || percentage > 100 as it just feels a bit easier to read. I can tweak it later. No biggie.

@jnunemaker
Copy link
Collaborator

🎉

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

Successfully merging this pull request may close these issues.

2 participants