-
Notifications
You must be signed in to change notification settings - Fork 769
Use InjectableValue to provide response info #716
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| package org.kohsuke.github; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JacksonInject; | ||
| import com.fasterxml.jackson.annotation.JsonCreator; | ||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
|
|
@@ -289,14 +290,11 @@ public static class Record { | |
|
|
||
| /** | ||
| * The time at which the rate limit will reset. This value is calculated based on | ||
| * {@link #getResetEpochSeconds()} by calling {@link #recalculateResetDate}. If the clock on the local machine | ||
| * not synchronized with the server clock, this time value will be adjusted to match the local machine's clock. | ||
| * <p> | ||
| * Recalculated by calling {@link #recalculateResetDate}. | ||
| * </p> | ||
| * {@link #getResetEpochSeconds()} by calling {@link #calculateResetDate}. If the clock on the local machine not | ||
| * synchronized with the server clock, this time value will be adjusted to match the local machine's clock. | ||
| */ | ||
| @Nonnull | ||
| private Date resetDate; | ||
| private final Date resetDate; | ||
|
|
||
| /** | ||
| * Instantiates a new Record. | ||
|
|
@@ -308,31 +306,37 @@ public static class Record { | |
| * @param resetEpochSeconds | ||
| * the reset epoch seconds | ||
| */ | ||
| @JsonCreator | ||
| public Record(@JsonProperty(value = "limit", required = true) int limit, | ||
| @JsonProperty(value = "remaining", required = true) int remaining, | ||
| @JsonProperty(value = "reset", required = true) long resetEpochSeconds) { | ||
| this(limit, remaining, resetEpochSeconds, null); | ||
| } | ||
|
|
||
| /** | ||
| * Instantiates a new Record. | ||
| * Instantiates a new Record. Called by Jackson data binding or during header parsing. | ||
| * | ||
| * @param limit | ||
| * the limit | ||
| * @param remaining | ||
| * the remaining | ||
| * @param resetEpochSeconds | ||
| * the reset epoch seconds | ||
| * @param updatedAt | ||
| * the updated at | ||
| * @param responseInfo | ||
| * the response info | ||
| */ | ||
| @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "Deprecated") | ||
| public Record(int limit, int remaining, long resetEpochSeconds, @CheckForNull String updatedAt) { | ||
| @JsonCreator | ||
| Record(@JsonProperty(value = "limit", required = true) int limit, | ||
| @JsonProperty(value = "remaining", required = true) int remaining, | ||
| @JsonProperty(value = "reset", required = true) long resetEpochSeconds, | ||
| @JacksonInject @CheckForNull GitHubResponse.ResponseInfo responseInfo) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, since we now have the the |
||
| this.limit = limit; | ||
| this.remaining = remaining; | ||
| this.resetEpochSeconds = resetEpochSeconds; | ||
| this.resetDate = recalculateResetDate(updatedAt); | ||
| String updatedAt = null; | ||
| if (responseInfo != null) { | ||
| updatedAt = responseInfo.headerField("Date"); | ||
| } | ||
| this.resetDate = calculateResetDate(updatedAt); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -362,7 +366,8 @@ public Record(int limit, int remaining, long resetEpochSeconds, @CheckForNull St | |
| * a string date in RFC 1123 | ||
| * @return reset date based on the passed date | ||
| */ | ||
| Date recalculateResetDate(@CheckForNull String updatedAt) { | ||
| @Nonnull | ||
| private Date calculateResetDate(@CheckForNull String updatedAt) { | ||
| long updatedAtEpochSeconds = createdAtEpochSeconds; | ||
| if (!StringUtils.isBlank(updatedAt)) { | ||
| try { | ||
|
|
@@ -379,7 +384,7 @@ Date recalculateResetDate(@CheckForNull String updatedAt) { | |
| // This may seem odd but it results in an accurate or slightly pessimistic reset date | ||
| // based on system time rather than assuming the system time synchronized with the server | ||
| long calculatedSecondsUntilReset = resetEpochSeconds - updatedAtEpochSeconds; | ||
| return resetDate = new Date((createdAtEpochSeconds + calculatedSecondsUntilReset) * 1000); | ||
| return new Date((createdAtEpochSeconds + calculatedSecondsUntilReset) * 1000); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,11 @@ | ||
| package org.kohsuke.github; | ||
|
|
||
| import com.fasterxml.jackson.databind.DeserializationFeature; | ||
| import com.fasterxml.jackson.databind.InjectableValues; | ||
| import com.fasterxml.jackson.databind.MapperFeature; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.ObjectReader; | ||
| import com.fasterxml.jackson.databind.ObjectWriter; | ||
| import com.fasterxml.jackson.databind.PropertyNamingStrategy; | ||
| import com.fasterxml.jackson.databind.introspect.VisibilityChecker; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
@@ -73,7 +76,7 @@ abstract class GitHubClient { | |
|
|
||
| private static final Logger LOGGER = Logger.getLogger(GitHubClient.class.getName()); | ||
|
|
||
| static final ObjectMapper MAPPER = new ObjectMapper(); | ||
| private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
| static final String GITHUB_URL = "https://api.github.com"; | ||
|
|
||
| private static final String[] TIME_FORMATS = { "yyyy/MM/dd HH:mm:ss ZZZZ", "yyyy-MM-dd'T'HH:mm:ss'Z'", | ||
|
|
@@ -399,7 +402,6 @@ private static <T> GitHubResponse<T> createResponse(@Nonnull GitHubResponse.Resp | |
| // Maybe throw an exception instead? | ||
| } else if (handler != null) { | ||
| body = handler.apply(responseInfo); | ||
| setResponseHeaders(responseInfo, body); | ||
| } | ||
| return new GitHubResponse<>(responseInfo, body); | ||
| } | ||
|
|
@@ -443,30 +445,6 @@ private static IOException interpretApiError(IOException e, | |
| return e; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the response headers on objects that need it. Ideally this would be handled by the objects themselves, but | ||
| * currently they do not have access to {@link GitHubResponse.ResponseInfo} after the | ||
| * | ||
| * @param responseInfo | ||
| * the response info | ||
| * @param readValue | ||
| * the object to consider adding headers to. | ||
| * @param <T> | ||
| * type of the object | ||
| */ | ||
| private static <T> void setResponseHeaders(GitHubResponse.ResponseInfo responseInfo, T readValue) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the special case code that we had before. |
||
| if (readValue instanceof GHObject[]) { | ||
| for (GHObject ghObject : (GHObject[]) readValue) { | ||
| ghObject.responseHeaderFields = responseInfo.headers(); | ||
| } | ||
| } else if (readValue instanceof GHObject) { | ||
| ((GHObject) readValue).responseHeaderFields = responseInfo.headers(); | ||
| } else if (readValue instanceof JsonRateLimit) { | ||
| // if we're getting a GHRateLimit it needs the server date | ||
| ((JsonRateLimit) readValue).resources.getCore().recalculateResetDate(responseInfo.headerField("Date")); | ||
| } | ||
| } | ||
|
|
||
| protected static boolean isRateLimitResponse(@Nonnull GitHubResponse.ResponseInfo responseInfo) { | ||
| return responseInfo.statusCode() == HttpURLConnection.HTTP_FORBIDDEN | ||
| && "0".equals(responseInfo.headerField("X-RateLimit-Remaining")); | ||
|
|
@@ -567,7 +545,7 @@ private void noteRateLimit(@Nonnull GitHubResponse.ResponseInfo responseInfo) { | |
| return; | ||
| } | ||
|
|
||
| GHRateLimit.Record observed = new GHRateLimit.Record(limit, remaining, reset, responseInfo.headerField("Date")); | ||
| GHRateLimit.Record observed = new GHRateLimit.Record(limit, remaining, reset, responseInfo); | ||
|
|
||
| updateCoreRateLimit(observed); | ||
| } | ||
|
|
@@ -714,4 +692,45 @@ static String printDate(Date dt) { | |
| df.setTimeZone(TimeZone.getTimeZone("GMT")); | ||
| return df.format(dt); | ||
| } | ||
|
|
||
| /** | ||
| * Gets an {@link ObjectWriter}. | ||
| * | ||
| * @return an {@link ObjectWriter} instance that can be further configured. | ||
| */ | ||
| @Nonnull | ||
| static ObjectWriter getMappingObjectWriter() { | ||
| return MAPPER.writer(); | ||
| } | ||
|
|
||
| /** | ||
| * Helper for {@link #getMappingObjectReader(GitHubResponse.ResponseInfo)} | ||
| * | ||
| * @return an {@link ObjectReader} instance that can be further configured. | ||
| */ | ||
| @Nonnull | ||
| static ObjectReader getMappingObjectReader() { | ||
| return getMappingObjectReader(null); | ||
| } | ||
|
|
||
| /** | ||
| * Gets an {@link ObjectReader}. | ||
| * | ||
| * Members of {@link InjectableValues} must be present even if {@code null}, otherwise classes expecting those | ||
| * values will fail to read. This differs from regular JSONProperties which provide defaults instead of failing. | ||
| * | ||
| * Having one spot to create readers and having it take all injectable values is not a great long term solution but | ||
| * it is sufficient for this first cut. | ||
| * | ||
| * @param responseInfo | ||
| * the {@link GitHubResponse.ResponseInfo} to inject for this reader. | ||
| * @return an {@link ObjectReader} instance that can be further configured. | ||
| */ | ||
| @Nonnull | ||
| static ObjectReader getMappingObjectReader(@CheckForNull GitHubResponse.ResponseInfo responseInfo) { | ||
| InjectableValues.Std inject = new InjectableValues.Std(); | ||
| inject.addValue(GitHubResponse.ResponseInfo.class, responseInfo); | ||
|
|
||
| return MAPPER.reader(inject); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what we do instead of the special case.