From 266bb15ac173fb4c5be4e4d17a62b522bc3bfb6a Mon Sep 17 00:00:00 2001 From: Adam Duston Date: Mon, 16 Oct 2017 11:06:07 -0400 Subject: [PATCH 1/4] Added method to get raw response. Necessary to get (for example) the body of an Attachment. --- src/main/java/com/force/api/ForceApi.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/com/force/api/ForceApi.java b/src/main/java/com/force/api/ForceApi.java index 0e96528..d4601d3 100644 --- a/src/main/java/com/force/api/ForceApi.java +++ b/src/main/java/com/force/api/ForceApi.java @@ -86,6 +86,12 @@ public ResourceRepresentation get(String path) { jsonMapper); } + public HttpResponse getResponse(String path) { + return apiRequest(new HttpRequest() + .url(uriBase()+path) + .method("GET")); + } + /** * sends a custom REST API DELETE request * From bdd6878b131a2b764460730cb5773f24c26d2269 Mon Sep 17 00:00:00 2001 From: Adam Duston Date: Mon, 16 Oct 2017 11:31:18 -0400 Subject: [PATCH 2/4] When calling getResponse, express path relative to the API endpoint --- src/main/java/com/force/api/ForceApi.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/force/api/ForceApi.java b/src/main/java/com/force/api/ForceApi.java index d4601d3..79a6f22 100644 --- a/src/main/java/com/force/api/ForceApi.java +++ b/src/main/java/com/force/api/ForceApi.java @@ -86,9 +86,9 @@ public ResourceRepresentation get(String path) { jsonMapper); } - public HttpResponse getResponse(String path) { + public HttpResponse getResponse(String completePath) { return apiRequest(new HttpRequest() - .url(uriBase()+path) + .url(session.getApiEndpoint() + completePath) .method("GET")); } From e10244df4e806f5d81350bf95b10e3b950e5bcfa Mon Sep 17 00:00:00 2001 From: Adam Duston Date: Fri, 20 Oct 2017 17:19:23 -0400 Subject: [PATCH 3/4] Get API limit info from response headers --- build.sbt | 12 +++++ project/build.properties | 1 + project/plugins.sbt | 1 + src/main/java/com/force/api/ForceApi.java | 51 ++++++++++++++----- src/main/java/com/force/api/http/Http.java | 7 +-- .../java/com/force/api/http/HttpResponse.java | 11 ++-- 6 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 build.sbt create mode 100644 project/build.properties create mode 100644 project/plugins.sbt diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..2b6c1e5 --- /dev/null +++ b/build.sbt @@ -0,0 +1,12 @@ +name := "force-rest-api" +organization := "com.frejo" +version := "1.0.0" +scalaVersion := "2.11.8" + +libraryDependencies ++= Seq( + "org.slf4j" % "slf4j-api" % "1.7.9", + "com.fasterxml.jackson.core" % "jackson-core" % "2.9.0", + "com.fasterxml.jackson.datatype" % "jackson-datatype-joda" % "2.9.0" +) + +publishTo := Some("ForceRestApi" at "s3://s3-us-east-1.amazonaws.com/docurated-build/force-rest-api") \ No newline at end of file diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..c091b86 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.13.16 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..a5b3904 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("com.frugalmechanic" % "fm-sbt-s3-resolver" % "0.9.0") \ No newline at end of file diff --git a/src/main/java/com/force/api/ForceApi.java b/src/main/java/com/force/api/ForceApi.java index 79a6f22..8cb1cd4 100644 --- a/src/main/java/com/force/api/ForceApi.java +++ b/src/main/java/com/force/api/ForceApi.java @@ -12,6 +12,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,22 +43,23 @@ * */ public class ForceApi { + private final static String SFORCE_LIMIT_HEADER = "Sforce-Limit-Info"; + private final static Pattern sforceLimitPattern = Pattern.compile("api-usage=(\\d+)/(\\d+)"); + private static final Logger logger = LoggerFactory.getLogger(ForceApi.class); private final ObjectMapper jsonMapper; - - private static final Logger logger = LoggerFactory.getLogger(ForceApi.class); - final ApiConfig config; - ApiSession session; - private boolean autoRenew = false; + private final boolean autoRenew; + private final AtomicInteger apiCallsUsed = new AtomicInteger(-1); + private final AtomicInteger apiLimit = new AtomicInteger(-1); + + ApiSession session; public ForceApi(ApiConfig config, ApiSession session) { this.config = config; jsonMapper = config.getObjectMapper(); this.session = session; - if(session.getRefreshToken()!=null) { - autoRenew = true; - } + this.autoRenew = session.getRefreshToken() != null; } public ForceApi(ApiSession session) { @@ -422,6 +426,14 @@ public DescribeSObject describeSObject(String sobject) { throw new ResourceException(e); } } + + public int getApiCallsUsed() { + return apiCallsUsed.get(); + } + + public int getApiLimit() { + return apiLimit.get(); + } private final String uriBase() { return(session.getApiEndpoint()+"/services/data/"+config.getApiVersionString()); @@ -430,7 +442,7 @@ private final String uriBase() { private final HttpResponse apiRequest(HttpRequest req) { req.setAuthorization("Bearer "+session.getAccessToken()); HttpResponse res = Http.send(req); - if(res.getResponseCode()==401) { + if (res.getResponseCode()==401) { // Perform one attempt to auto renew session if possible if (autoRenew) { logger.debug("Session expired. Refreshing session..."); @@ -446,19 +458,34 @@ private final HttpResponse apiRequest(HttpRequest req) { res = Http.send(req); } } - if(res.getResponseCode()>299) { - if(res.getResponseCode()==401) { + if (res.getResponseCode()>299) { + if (res.getResponseCode()==401) { throw new ApiTokenException(res.getString()); } else { throw new ApiException(res.getResponseCode(), res.getString()); } - } else if(req.getExpectedCode()!=-1 && res.getResponseCode()!=req.getExpectedCode()) { + } else if (req.getExpectedCode()!=-1 && res.getResponseCode()!=req.getExpectedCode()) { throw new RuntimeException("Unexpected response from Force API. Got response code "+res.getResponseCode()+ ". Was expecting "+req.getExpectedCode()); } else { + setApiLimitInfo(res.getHeaders()); return res; } } + + private void setApiLimitInfo(Map> headers) { + if (!headers.containsKey(SFORCE_LIMIT_HEADER)) { + return; + } + String header = headers.get(SFORCE_LIMIT_HEADER).get(0); + Matcher matcher = sforceLimitPattern.matcher(header); + if (matcher.find()) { + apiCallsUsed.set(Integer.parseInt(matcher.group(1))); + apiLimit.set(Integer.parseInt(matcher.group(2))); + } else { + logger.warn("Unable to find API limit info in header {}", header); + } + } /** * Normalizes the JSON response in case it contains responses from diff --git a/src/main/java/com/force/api/http/Http.java b/src/main/java/com/force/api/http/Http.java index 0e1293a..9cc8ccb 100644 --- a/src/main/java/com/force/api/http/Http.java +++ b/src/main/java/com/force/api/http/Http.java @@ -91,13 +91,14 @@ public static final HttpResponse send(HttpRequest req) { switch (req.getResponseFormat()) { case BYTE: return new HttpResponse().setByte(readResponse(conn.getInputStream())) - .setResponseCode(code); + .setResponseCode(code).setHeaders(conn.getHeaderFields()); case STRING: return new HttpResponse().setString( new String(readResponse(conn.getInputStream()), "UTF-8")).setResponseCode( - code); + code).setHeaders(conn.getHeaderFields()); default: - return new HttpResponse().setStream(conn.getInputStream()).setResponseCode(code); + return new HttpResponse().setStream(conn.getInputStream()).setResponseCode(code) + .setHeaders(conn.getHeaderFields()); } } else { logger.info("Bad response code: {} on request: {}", code, req); diff --git a/src/main/java/com/force/api/http/HttpResponse.java b/src/main/java/com/force/api/http/HttpResponse.java index cf01f7f..42b0445 100644 --- a/src/main/java/com/force/api/http/HttpResponse.java +++ b/src/main/java/com/force/api/http/HttpResponse.java @@ -1,6 +1,8 @@ package com.force.api.http; import java.io.InputStream; +import java.util.List; +import java.util.Map; public class HttpResponse { @@ -8,6 +10,7 @@ public class HttpResponse { private byte[] byteResponse; private InputStream streamResponse; private int responseCode; + private Map> headers; public int getResponseCode() { return responseCode; @@ -21,6 +24,7 @@ public byte[] getByte() { public InputStream getStream() { return streamResponse; } + public Map> getHeaders() { return headers; } public HttpResponse setString(String stringResponse) { this.stringResponse = stringResponse; return this; @@ -38,8 +42,9 @@ public HttpResponse setResponseCode(int value) { responseCode = value; return this; } - - - + public HttpResponse setHeaders(Map> headers) { + this.headers = headers; + return this; + } } From da90a6f10c004509344ea3eaeba339c5f569543a Mon Sep 17 00:00:00 2001 From: Adam Duston Date: Mon, 23 Oct 2017 09:42:04 -0400 Subject: [PATCH 4/4] Added some important javadocs --- build.sbt | 2 +- src/main/java/com/force/api/ForceApi.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 2b6c1e5..d4789c2 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ name := "force-rest-api" organization := "com.frejo" -version := "1.0.0" +version := "1.0.1" scalaVersion := "2.11.8" libraryDependencies ++= Seq( diff --git a/src/main/java/com/force/api/ForceApi.java b/src/main/java/com/force/api/ForceApi.java index 8cb1cd4..550ea41 100644 --- a/src/main/java/com/force/api/ForceApi.java +++ b/src/main/java/com/force/api/ForceApi.java @@ -427,10 +427,22 @@ public DescribeSObject describeSObject(String sobject) { } } + /** + * This value is initialized to -1 and then is set to a non-negative value + * after the first Limit Info Header is received from a Salesforce HTTP + * response. + * @return + */ public int getApiCallsUsed() { return apiCallsUsed.get(); } + /** + * This value is initialized to -1 and then is set to a non-negative value + * after the first Limit Info Header is received from a Salesforce HTTP + * response. + * @return + */ public int getApiLimit() { return apiLimit.get(); }