There are two types of logging used by the Smartsheet Java SDK:
The console logger logs REST API traffic directly to the console. The console logger is verbose, and as such is best used when developing new code or features. To use the console logger, set two system properties:
System.setProperty("Smartsheet.trace.parts", "RequestBodySummary, ResponseBodySummary");
System.setProperty("Smartsheet.trace.pretty", "true");
Smartsheet.trace.pretty - if true
, formats log messages for improved JSON readability.
If Smartsheet.trace.pretty
is false
the console logger will use a compact format.
Smartsheet.trace.parts - determines what portions of the API traffic are logged. Valid trace parts entries include:
- RequestHeaders
- RequestBody
- RequestBodySummary
- ResponseHeaders
- ResponseBody
- ResponseBodySummary
- Request (RequestHeaders + RequestBodySummary)
- Response (ResponseHeaders + ResponseBodySummary)
By default, console log entries are truncated at 1024 characters. You can change the truncation limit by defining a
system property Smartsheet.trace.truncateLen
and setting it equal to the desired truncation limit, for example:
System.setProperty("Smartsheet.trace.truncateLen", "512");
The Smartsheet Java SDK also has a dependency on the SLF4J facade. SLF4J is configurable at or post distribution and is meant for production environments. More information about SLF4J and the supported logging frameworks is available here.
Using SLF4J, the Smartsheet Java SDK logs all API queries including HTTP method, URI, HTTP status and response time
to INFO
. API calls that fail (HTTP status != 200) are fully logged (request, response and full bodies) to WARN
.
Finally, successful (HTTP status 200) request and response summaries are logged to DEBUG
.
The build file for the Smartsheet Java SDK also includes a test only dependency on the slf4j-simple
logging framework.
Details on how to configure logging are framework dependant, however, a usage example for the Simple logger can be
found in the simplelogger.properties file in the Sample folder. Alternately, a usage example for Log4j can
be found in the java-read-write-sheet example here.
If there is an API feature that is not yet supported by the Java SDK, there is a passthrough option that allows you to pass and receive raw JSON objects.
To invoke the passthrough, your code can call one of the following four methods:
jsonResponse = smartsheet.passthroughResources().postRequest(endpoint, payload, parameters);
jsonResponse = smartsheet.passthroughResources().getRequest(endpoint, parameters);
jsonResponse = smartsheet.passthroughResources().putRequest(endpoint, payload, parameters);
jsonResponse = smartsheet.passthroughResources().deleteRequest(endpoint);
endpoint (String)
: The specific API endpoint you wish to invoke. The client object base URL gets prepended to the caller’s endpoint URL argument, e.g., if endpoint is 'sheets' an HTTP GET is requested from the URL https://api.smartsheet.com/2.0/sheetspayload (String)
: The data to be passed through in the request payload as a string.query_params (Hashmap<String, Object>)
: An optional list of query parameters.
All calls to passthrough methods return a JSON string result.
The following example shows how to POST data to https://api.smartsheet.com/2.0/sheets using the passthrough method and a JSON string payload:
String payload =
"{\"name\": \"my new sheet\"," +
"\"columns\": [" +
"{\"title\": \"Favorite\", \"type\": \"CHECKBOX\", \"symbol\": \"STAR\"}," +
"{\"title\": \"Primary Column\", \"primary\": true, \"type\": \"TEXT_NUMBER\"}" +
"]" +
"}";
String jsonResponse = smartsheet.passthroughResources().postRequest("sheets", payload, null);
Unit tests:
./gradlew test
Integration tests:
- Store an access token in your environment as SMARTSHEET_ACCESS_TOKEN
./gradlew integrationTest
Mock API tests:
- Clone the Smartsheet sdk tests repo and follow the instructions from the readme to start the mock server.
./gradlew sdkTest
Google doesn’t support the Apache HTTP Client on Android (used as the default HTTP client by the SDK). In order to make it easier to use the Smartsheet Java SDK, the SDK contains a 2nd HTTP client class, AndroidHttpClient. The AndroidHttpClient class is included with version 2.68.4+ of the SDK. To use the Smartsheet Java SDK on Android, follow these steps:
- Add to the module-level build.gradle's dependencies section:
implementation 'com.smartsheet:smartsheet-sdk-java:2.68.4'
- Add to the module-level build.gradle's android section:
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
- When you invoke the Smartsheet client, instruct it to use the AndroidHttpClient to access the Smartsheet API:
Smartsheet smartsheet = SmartsheetFactory.custom().setHttpClient(new AndroidHttpClient())
.setAccessToken("[TOKEN]").build();
You can provide a number of customizations to the default HTTP behavior by extending the DefaultHttpClient class and overriding one or more methods (examples below). If required, you can remove use of the Apache HTTP Client by implementing the HttpClient interface in a custom client (see Android QRScanner).
Common customizations may include:
- implementing an HTTP proxy
- injecting additional HTTP headers
- overriding default timeout or retry behavior
The following example shows how to enable a proxy by providing the SmartsheetBuilder with an HttpClient which extends DefaultHttpClient.
Invoke the SmartsheetBuilder with a custom HttpClient:
ProxyHttpClient proxyHttpClient = new ProxyHttpClient("localhost", 8080);
Smartsheet smartsheet = SmartsheetFactory.custom().setHttpClient(proxyHttpClient).build();
import com.smartsheet.api.internal.http.DefaultHttpClient;
import com.smartsheet.api.internal.http.HttpRequest;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpRequestBase;
public class ProxyHttpClient extends DefaultHttpClient {
private String proxyHost;
private Integer proxyPort;
public ProxyHttpClient(String proxyHost, Integer proxyPort) {
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
/** Override this method to inject additional headers, or setup proxy information
* on the request.
*/
@Override
public HttpRequestBase createApacheRequest(HttpRequest smartsheetRequest) {
HttpRequestBase apacheHttpRequest = super.createApacheRequest(smartsheetRequest);
RequestConfig.Builder builder = RequestConfig.custom();
if (apacheHttpRequest.getConfig() != null) {
builder = RequestConfig.copy(apacheHttpRequest.getConfig());
}
HttpHost proxy = new HttpHost(proxyHost, proxyPort, "http");
builder.setProxy(proxy);
RequestConfig config = builder.build();
apacheHttpRequest.setConfig(config);
return apacheHttpRequest;
}
}
The following example shows how to override the default retry/timeout logic.
Invoke the SmartsheetBuilder with a custom HttpClient:
Smartsheet smartsheet = SmartsheetFactory.custom().setHttpClient(new RetryHttpClient()).build();
smartsheet.setMaxRetryTimeMillis(30000);
import com.smartsheet.api.internal.http.DefaultHttpClient;
import com.smartsheet.api.internal.http.HttpResponse;
import com.smartsheet.api.models.Error;
import java.io.IOException;
public class RetryHttpClient extends DefaultHttpClient {
/**
* Override this method to perform API requests for special cases
*/
@Override
public boolean shouldRetry(int previousAttempts, long totalElapsedTimeMillis, HttpResponse response) {
// HTTP Status available as response.getStatusCode()
int httpStatus = response.getStatusCode();
String contentType = response.getEntity().getContentType();
if (contentType != null && !contentType.startsWith(JSON_MIME_TYPE)) {
// it's not JSON; don't even try to parse it
return false;
}
Error error;
try {
// Details about the Smartsheet API error condition
error = jsonSerializer.deserialize(Error.class, response.getEntity().getContent());
}
catch (IOException e) {
return false;
}
switch(error.getErrorCode()) {
// The default shouldRetry, retries 4001, 4002, 4003, 4004 codes
case 4001:
case 4002:
case 4003:
case 4004:
case 9999: // adding my fictional error code
break;
default:
return false;
}
// The default calcBackoff uses exponential backoff, add custom behavior by overriding calcBackoff
long backoffMillis = calcBackoff(previousAttempts, totalElapsedTimeMillis, error);
if (backoffMillis < 0)
return false;
logger.info("HttpError StatusCode=" + response.getStatusCode() + ": Retrying in " + backoffMillis + " milliseconds");
try {
Thread.sleep(backoffMillis);
}
catch (InterruptedException e) {
logger.warn("sleep interrupted", e);
return false;
}
return true;
}
}
The following sample demonstrates best practices for consuming the event stream from the Smartsheet Event Reporting feature.
The sample uses the smartsheet.eventResources().listEvents
method to request a list of events from the stream. The
first request sets the since
parameter with the point in time (i.e. event occurrence datetime) in the stream from
which to start consuming events. The since
parameter can be set with a datetime value that is either formatted as
ISO 8601 (e.g. 2010-01-01T00:00:00Z) or as UNIX epoch (in which case the numericDates
parameter must also be set to
true
. By default the numericDates
parameter is set to false
).
To consume the next list of events after the initial list of events is returned, set the streamPosition
parameter
with the nextStreamPosition
property obtained from the previous request and don't set the since
parameter with
any values. This is because when using the listEvents
method, either the since
parameter or the streamPosition
parameter should be set, but never both.
Note that the moreAvailable
property in a response indicates whether more events are immediately available for
consumption. If events are not immediately available, they may still be generating so subsequent requests should keep
using the same streamPosition
value until the next list of events is retrieved.
Many events have additional information available as part of the event. That information can be accessed using the
HashMap stored in the additionalDetails
property. Information about the additional details provided can be found
here.
public class Sample {
public static void main(String[] args) throws SmartsheetException {
SampleProgram();
}
// this example is looking specifically for new sheet events
private static void printNewSheetEventsInList(List<Event> events)
{
// enumerate all events in the list of returned events
for (Event event: events) {
// find all created sheets
if (event.getObjectType() == EventObjectType.SHEET && event.getAction() == EventAction.CREATE) {
// additional details are available for some events, they can be accessed as a HashMap
// in the additionalDetails property
System.out.println(event.getAdditionalDetails().get("sheetName"));
}
}
}
public static void SampleProgram() throws SmartsheetException{
Smartsheet smartsheet = SmartsheetFactory.createDefaultClient();
// begin listing events in the stream starting with the `since` parameter
Date lastWeek = new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7));
// this example looks at the previous 7 days of events by providing a since argument set to last week's date
EventResult eventResult = smartsheet.eventResources().listEvents(lastWeek, null, 1000, false);
printNewSheetEventsInList(eventResult.getData());
// continue listing events in the stream by using the `streamPosition`, if the previous response indicates
// that more data is available.
while(eventResult.getMoreAvailable()) {
eventResult = smartsheet.eventResources().listEvents(null, eventResult.getNextStreamPosition(), 10000, true);
printNewSheetEventsInList(eventResult.getData());
}
}
}
If you need to access Smartsheetgov you will need to specify the Smartsheetgov API URI as the base URI during creation of the Smartsheet client object. SmartsheetGov uses a base URI of https://api.smartsheetgov.com/2.0/. The base URI is defined as a constant in both the SmartsheetBuilder and SmartsheetFactory classes (i.e. SmartsheetFactory.GOV_BASE_URI). The SmartsheetFactory also contains API to create default Smartsheet clients which point to the Smartsheetgov URI:
package com.smartsheet.api.sample;
import com.smartsheet.api.Smartsheet;
import com.smartsheet.api.SmartsheetException;
import com.smartsheet.api.SmartsheetFactory;
import com.smartsheet.api.models.Column;
import com.smartsheet.api.models.PagedResult;
import com.smartsheet.api.models.Row;
import com.smartsheet.api.models.Sheet;
import java.util.List;
/**
*
*/
public class Sample {
static {
// Uncomment these lines to enable logging to console
// System.setProperty("Smartsheet.trace.parts", "RequestBody,ResponseBodySummary");
// System.setProperty("Smartsheet.trace.pretty", "true");
}
public static void main(String[] args) {
try {
// Create Smartsheet client
// Set your access token in environment variable "SMARTSHEET_ACCESS_TOKEN", else update and uncomment here
Smartsheet smartsheet = SmartsheetFactory.createDefaultGovAccountClient( /* "ll352u9jujauoqz4gstvsae05" */);
// List all sheets
PagedResult<Sheet> sheets = smartsheet.sheetResources().listSheets();
System.out.println("\nFound " + sheets.getTotalCount() + " sheets\n");
Long sheetId = sheets.getData().get(0).getId(); // Default to first sheet
// TODO: Uncomment if you wish to read a specific sheet
// sheetId = 239236234L;
// Load entire sheet
Sheet sheet = smartsheet.sheetResources().getSheet(sheetId);
List<Row> rows = sheet.getRows();
System.out.println("\nLoaded sheet id " + sheetId + " with " + rows.size() + " rows, title: " + sheet.getName());
// Display the first 5 rows & columns
for (int rowNumber = 0; rowNumber < rows.size() && rowNumber < 5; rowNumber++)
DumpRow(rows.get(rowNumber), sheet.getColumns());
} catch (SmartsheetException sx) {
sx.printStackTrace();
}
System.out.println("done.");
}
static void DumpRow(Row row, List<Column> columns)
{
System.out.println("Row # " + row.getRowNumber() + ":");
for (int columnNumber = 0; columnNumber < columns.size() && columnNumber < 5; columnNumber++) {
System.out.println(" " + columns.get(columnNumber).getTitle() + ": " + row.getCells().get(columnNumber).getValue());
}
}
}
If you need to access Smartsheet Regions Europe you will need to specify the Smartsheet.eu API URI as the base URI during creation of the Smartsheet client object. Smartsheet.eu uses a base URI of https://api.smartsheet.eu/2.0/. The base URI is defined as a constant in both the SmartsheetBuilder and SmartsheetFactory classes (i.e. SmartsheetFactory.EU_BASE_URI). The SmartsheetFactory also contains API to create default Smartsheet clients which point to the Smartsheet.eu URI:
package com.smartsheet.api.sample;
import com.smartsheet.api.Smartsheet;
import com.smartsheet.api.SmartsheetException;
import com.smartsheet.api.SmartsheetFactory;
import com.smartsheet.api.models.Column;
import com.smartsheet.api.models.PagedResult;
import com.smartsheet.api.models.Row;
import com.smartsheet.api.models.Sheet;
import java.util.List;
/**
*
*/
public class Sample {
static {
// Uncomment these lines to enable logging to console
// System.setProperty("Smartsheet.trace.parts", "RequestBody,ResponseBodySummary");
// System.setProperty("Smartsheet.trace.pretty", "true");
}
public static void main(String[] args) {
try {
// Create Smartsheet client
// Set your access token in environment variable "SMARTSHEET_ACCESS_TOKEN", else update and uncomment here
Smartsheet smartsheet = SmartsheetFactory.createDefaultEUAccountClient( /* "ll352u9jujauoqz4gstvsae05" */);