Skip to content

Allow to retrieve Rate Limit headers#153

Merged
cocojoe merged 8 commits intoauth0:masterfrom
rvillablanca:rate-limits
Sep 25, 2018
Merged

Allow to retrieve Rate Limit headers#153
cocojoe merged 8 commits intoauth0:masterfrom
rvillablanca:rate-limits

Conversation

@rvillablanca
Copy link
Copy Markdown
Contributor

@rvillablanca rvillablanca commented Sep 22, 2018

This PR resolve #99 to allow get the rate limit values from X-RateLimit-* headers.
I have made some decisions for default values, for example, when an http response code with value 429 is received then the information about rate limits should be recovered from headers X-RateLimit-*, but I don't know if these values are always sent so, if I couldn't get some header then a default -1 will be assigned.
The primitive data type chosen was long since the X-RateLimit-Reset is a Unix timestamp and the others values are normal numbers.

Copy link
Copy Markdown
Contributor

@lbalmaceda lbalmaceda left a comment

Choose a reason for hiding this comment

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

@rvillablanca Thanks for your PR! I've left a few comments. 🎉

}

protected Auth0Exception createResponseException(Response response) {
if (response.code() == 429) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Move the 429 to a private constant like STATUS_CODE_TOO_MANY_REQUEST

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

long limit = parseRateLimitHeader("X-RateLimit-Limit", response);
long remaining = parseRateLimitHeader("X-RateLimit-Remaining", response);
long reset = parseRateLimitHeader("X-RateLimit-Reset", response);
return new RateLimitException(limit, remaining, reset, "Rate limits reached");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd make the parseRateLimitHeader method parse all the headers at the same time and then throw the exception. Maybe something like this:

if (code == 429){
   throw buildRateLimitException(response);
}

Also, seems like a rate limit exception would always have the same message/description. Let's remove that argument from the constructor and just keep the numbers.

I'm not so sure about this, but I think the message should be "Rate limit reached". (no "limits")

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

In this case I didn't want to couple the exception and the response so I modified the method to parse the headers and create the exception

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I didn't want to couple the exception and the response

That was a good call 👍

private final long reset;

public RateLimitException(long limit, long remaining, long reset, String message) {
super(message, 429, null);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Message can be "hardcoded" here. No need for an arg.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

unused import

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

server.enqueue(response);
}

public void rateLimitResponse() throws IOException {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'd call this rateLimitReachedResponse and pass the 3 values on the args. This way, you can test how defaults are being assigned if the header is missing (not just for 1 header)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

exception = e;
}
assertThat(exception, is(notNullValue()));
assertThat(exception, is(instanceOf(APIException.class)));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No need to test for APIException since RateLimitException is already a subclass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!


/**
* Represents a server error when a rate limit has been exceeded.
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please add a line linking to the docs. Such as

"To learn more about rate limits, visit https://auth0.com/docs/policies/rate-limits"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

this.reset = reset;
}

public long getLimit() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Javadoc for this 3 getters. You can use the docs to take some input. The idea is to explain in no more than a line what each of this numbers represent and most important, what value would be returned when the header is missing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done!

Copy link
Copy Markdown
Contributor

@lbalmaceda lbalmaceda left a comment

Choose a reason for hiding this comment

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

A few small comments. Please add 1 more test that asserts that when headers are not set, the returned value is -1


/**
* Getter for the maximum number of requests available in the current time frame.
* @return The maximun number of requests.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

typo: should be "maximum"

Please change to "The maximum number of requests or -1 if missing."

* Getters for {@code limit, remaining} and {@code reset} corresponds to {@code X-RateLimit-Limit, X-RateLimit-Remaining} and {@code X-RateLimit-Reset} HTTP headers.
* If the value of any headers is missing, then a default value -1 will assigned.
* <p>
* "To learn more about rate limits, visit <a href="https://auth0.com/docs/policies/rate-limits">https://auth0.com/docs/policies/rate-limits</a>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Remove the " at the beginning


/**
* Getter for the number of remaining requests in the current time frame.
* @return Number of remaining requests.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please change to "The number of remaining requests or -1 if missing."


/**
* Getter for the UNIX timestamp of the expected time when the rate limit will reset.
* @return The UNIX timestamp.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please change to "The UNIX timestamp or -1 if missing."

public void rateLimitReachedResponse(long limit, long remaining, long reset) throws IOException {
MockResponse response = new MockResponse()
.setResponseCode(429)
.addHeader("X-RateLimit-Limit", String.valueOf(limit))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You might want to add a check whether the arg is "-1" to avoid setting the header, so you can test that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

All suggestions have been done!

@lbalmaceda lbalmaceda added this to the 1.9.0 milestone Sep 25, 2018
@cocojoe cocojoe merged commit 66d189e into auth0:master Sep 25, 2018
@lbalmaceda lbalmaceda changed the title support to retrieve rate limits values from response headers #99 Allow to retrieve Rate Limit headers Sep 25, 2018
@rvillablanca rvillablanca deleted the rate-limits branch September 25, 2018 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Auth0 Rate Limits

3 participants