All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.smartsheet.api.internal.AbstractResources Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2024 Smartsheet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.smartsheet.api.internal;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.smartsheet.api.AuthorizationException;
import com.smartsheet.api.InvalidRequestException;
import com.smartsheet.api.ResourceNotFoundException;
import com.smartsheet.api.ServiceUnavailableException;
import com.smartsheet.api.SmartsheetException;
import com.smartsheet.api.SmartsheetRestException;
import com.smartsheet.api.internal.http.HttpEntity;
import com.smartsheet.api.internal.http.HttpMethod;
import com.smartsheet.api.internal.http.HttpRequest;
import com.smartsheet.api.internal.http.HttpResponse;
import com.smartsheet.api.internal.json.JSONSerializerException;
import com.smartsheet.api.internal.util.StreamUtil;
import com.smartsheet.api.internal.util.Util;
import com.smartsheet.api.models.Attachment;
import com.smartsheet.api.models.CopyOrMoveRowDirective;
import com.smartsheet.api.models.CopyOrMoveRowResult;
import com.smartsheet.api.models.PagedResult;
import com.smartsheet.api.models.Result;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This is the base class of the Smartsheet REST API resources.
 * 

* Thread Safety: This class is thread safe because it is immutable and the underlying SmartsheetImpl is thread safe. */ public abstract class AbstractResources { /** * this system property is used to control the number of characters logged from an API response in logs */ public static final String PROPERTY_RESPONSE_LOG_CHARS = "Smartsheet.responseLogChars"; private static final Logger log = LoggerFactory.getLogger(AbstractResources.class); /** * The Constant BUFFER_SIZE. */ private static final int BUFFER_SIZE = 4098; private static final String JSON_CONTENT_TYPE = "application/json"; private static final String HEADER_CONTENT_TYPE = "Content-Type"; /** * The Enum ErrorCode. */ public enum ErrorCode { BAD_REQUEST(400, InvalidRequestException.class), NOT_AUTHORIZED(401, AuthorizationException.class), FORBIDDEN(403, AuthorizationException.class), NOT_FOUND(404, ResourceNotFoundException.class), METHOD_NOT_SUPPORTED(405, InvalidRequestException.class), INTERNAL_SERVER_ERROR(500, InvalidRequestException.class), SERVICE_UNAVAILABLE(503, ServiceUnavailableException.class); /** * The error code. */ int errorCode; /** * The Exception class. */ Class exceptionClass; /** * Instantiates a new error code. * * @param errorCode the error code * @param exceptionClass the Exception class */ ErrorCode(int errorCode, Class exceptionClass) { this.errorCode = errorCode; this.exceptionClass = exceptionClass; } /** * Gets the error code. * * @param errorNumber the error number * @return the error code */ public static ErrorCode getErrorCode(int errorNumber) { for (ErrorCode code : ErrorCode.values()) { if (code.errorCode == errorNumber) { return code; } } return null; } /** * Gets the exception. * * @return the exception * @throws InstantiationException the instantiation exception * @throws IllegalAccessException the illegal access exception */ public SmartsheetRestException getException() throws InstantiationException, IllegalAccessException { return exceptionClass.newInstance(); } /** * Gets the exception. * * @param error the error * @return the exception * @throws SmartsheetException the smartsheet exception */ public SmartsheetRestException getException(com.smartsheet.api.models.Error error) throws SmartsheetException { try { return exceptionClass.getConstructor(com.smartsheet.api.models.Error.class).newInstance(error); } catch (IllegalArgumentException e) { throw new SmartsheetException(e); } catch (SecurityException e) { throw new SmartsheetException(e); } catch (InstantiationException e) { throw new SmartsheetException(e); } catch (IllegalAccessException e) { throw new SmartsheetException(e); } catch (InvocationTargetException e) { throw new SmartsheetException(e); } catch (NoSuchMethodException e) { throw new SmartsheetException(e); } } } /** * Represents the SmartsheetImpl. *

* It will be initialized in constructor and will not change afterwards. */ protected final SmartsheetImpl smartsheet; /** * Constructor. * * @param smartsheet the smartsheet */ protected AbstractResources(SmartsheetImpl smartsheet) { Util.throwIfNull(smartsheet); this.smartsheet = smartsheet; } /** * Get a resource from Smartsheet REST API. *

* Parameters: - path : the relative path of the resource - objectClass : the resource object class *

* Returns: the resource (note that if there is no such resource, this method will throw ResourceNotFoundException * rather than returning null). *

* Exceptions: - * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ResourceNotFoundException : if the resource can not be found * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param path the relative path of the resource. * @param objectClass the object class * @return the resource * @throws SmartsheetException the smartsheet exception */ protected T getResource(String path, Class objectClass) throws SmartsheetException { Util.throwIfNull(path, objectClass); if (path.isEmpty()) { com.smartsheet.api.models.Error error = new com.smartsheet.api.models.Error(); error.setMessage("An empty path was provided."); throw new ResourceNotFoundException(error); } HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.GET); T obj = null; String content = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); InputStream inputStream = response.getEntity().getContent(); switch (response.getStatusCode()) { case 200: try { if (log.isInfoEnabled()) { ByteArrayOutputStream contentCopyStream = new ByteArrayOutputStream(); inputStream = StreamUtil.cloneContent(inputStream, response.getEntity().getContentLength(), contentCopyStream); content = StreamUtil.toUtf8StringOrHex(contentCopyStream, getResponseLogLength()); } obj = this.smartsheet.getJsonSerializer().deserialize(objectClass, inputStream); } catch (JsonParseException e) { log.info("failure parsing '{}'", content, e); throw new SmartsheetException(e); } catch (JsonMappingException e) { log.info("failure mapping '{}'", content, e); throw new SmartsheetException(e); } catch (IOException e) { log.info("failure loading '{}'", content, e); throw new SmartsheetException(e); } break; default: handleError(response); } } catch (JSONSerializerException jsx) { log.info("failed to parse '{}'", content, jsx); throw jsx; } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * Create a resource using Smartsheet REST API. *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type of object to return/deserialize * @param the generic type of object to serialize * @param path the relative path of the resource collections * @param objectClass the resource object class * @param object the object to create * @return the created resource * @throws SmartsheetException the smartsheet exception */ protected T createResource(String path, Class objectClass, S object) throws SmartsheetException { Util.throwIfNull(path, object, objectClass); Util.throwIfEmpty(path); HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST); ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream(); this.smartsheet.getJsonSerializer().serialize(object, objectBytesStream); HttpEntity entity = new HttpEntity(); entity.setContentType(JSON_CONTENT_TYPE); entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray())); entity.setContentLength(objectBytesStream.size()); request.setEntity(entity); T obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: { InputStream inputStream = response.getEntity().getContent(); String content = null; try { if (log.isInfoEnabled()) { ByteArrayOutputStream contentCopyStream = new ByteArrayOutputStream(); inputStream = StreamUtil.cloneContent(inputStream, response.getEntity().getContentLength(), contentCopyStream); content = StreamUtil.toUtf8StringOrHex(contentCopyStream, getResponseLogLength()); } obj = this.smartsheet.getJsonSerializer().deserializeResult(objectClass, inputStream).getResult(); } catch (JSONSerializerException e) { log.info("failure parsing '{}'", content, e); throw new SmartsheetException(e); } catch (IOException e) { log.info("failure cloning content from inputStream '{}'", inputStream, e); throw new SmartsheetException(e); } break; } default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * Create a resource using Smartsheet REST API. *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param path the relative path of the resource collections * @param objectClass the resource object class * @param object the object to create * @return the created resource * @throws SmartsheetException the smartsheet exception */ protected T createResourceWithAttachment( String path, Class objectClass, T object, String partName, InputStream inputStream, String contentType, String attachmentName ) throws SmartsheetException { Util.throwIfNull(path, object); Util.throwIfEmpty(path); HttpRequest request; final String boundary = "----" + System.currentTimeMillis(); CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost uploadFile = createHttpPost(this.getSmartsheet().getBaseURI().resolve(path)); try { uploadFile.setHeader(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); } catch (Exception e) { throw new RuntimeException(e); } MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setBoundary(boundary); builder.addTextBody(partName, this.getSmartsheet().getJsonSerializer().serialize(object), ContentType.APPLICATION_JSON); builder.addBinaryBody("file", inputStream, ContentType.create(contentType), attachmentName); org.apache.http.HttpEntity multipart = builder.build(); uploadFile.setEntity(multipart); T obj = null; //implement switch case try { CloseableHttpResponse response = httpClient.execute(uploadFile); org.apache.http.HttpEntity responseEntity = response.getEntity(); obj = this.getSmartsheet().getJsonSerializer().deserializeResult(objectClass, responseEntity.getContent()).getResult(); } catch (Exception e) { throw new RuntimeException(e); } return obj; } /** * Update a resource using Smartsheet REST API. *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ResourceNotFoundException : if the resource can not be found * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param path the relative path of the resource * @param objectClass the resource object class * @param object the object to create * @return the updated resource * @throws SmartsheetException the smartsheet exception */ protected T updateResource(String path, Class objectClass, T object) throws SmartsheetException { Util.throwIfNull(path, object); Util.throwIfEmpty(path); HttpRequest request; request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.PUT); ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream(); this.smartsheet.getJsonSerializer().serialize(object, objectBytesStream); HttpEntity entity = new HttpEntity(); entity.setContentType(JSON_CONTENT_TYPE); entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray())); entity.setContentLength(objectBytesStream.size()); request.setEntity(entity); T obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeResult(objectClass, response.getEntity().getContent()).getResult(); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * List resources using Smartsheet REST API. *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param path the relative path of the resource collections * @param objectClass the resource object class * @return the resources * @throws SmartsheetException if an error occurred during the operation */ protected List listResources(String path, Class objectClass) throws SmartsheetException { Util.throwIfNull(path, objectClass); Util.throwIfEmpty(path); HttpRequest request; request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.GET); List obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeList(objectClass, response.getEntity().getContent()); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * List resources Wrapper (supports paging info) using Smartsheet REST API. * * @throws IllegalArgumentException : if any argument is null, or path is empty string * @throws InvalidRequestException : if there is any problem with the REST API request * @throws AuthorizationException : if there is any problem with the REST API authorization(access token) * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * @throws SmartsheetRestException : if there is any other REST API related error occurred during the operation * @throws SmartsheetException : if there is any other error occurred during the operation */ protected PagedResult listResourcesWithWrapper(String path, Class objectClass) throws SmartsheetException { Util.throwIfNull(path, objectClass); Util.throwIfEmpty(path); HttpRequest request; request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.GET); PagedResult obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeDataWrapper(objectClass, response.getEntity().getContent()); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * Delete a resource from Smartsheet REST API. *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ResourceNotFoundException : if the resource can not be found * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param path the relative path of the resource * @param objectClass the resource object class * @throws SmartsheetException the smartsheet exception */ protected void deleteResource(String path, Class objectClass) throws SmartsheetException { Util.throwIfNull(path, objectClass); Util.throwIfEmpty(path); HttpRequest request; request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.DELETE); try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: this.smartsheet.getJsonSerializer().deserializeResult(objectClass, response.getEntity().getContent()); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } } /** * Delete resources and return a list from Smartsheet REST API. *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ResourceNotFoundException : if the resource can not be found * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param path the relative path of the resource * @param objectClass the resource object class * @return List of ids deleted * @throws SmartsheetException the smartsheet exception */ protected List deleteListResources(String path, Class objectClass) throws SmartsheetException { Util.throwIfNull(path, objectClass); Util.throwIfEmpty(path); Result> obj = null; HttpRequest request; request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.DELETE); try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeListResult(objectClass, response.getEntity().getContent()); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj.getResult(); } /** * Post an object to Smartsheet REST API and receive a list of objects from response. *

* Parameters: - path : the relative path of the resource collections - objectToPost : the object to post - * objectClassToReceive : the resource object class to receive *

* Returns: the object list *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param the generic type * @param the generic type * @param path the path * @param objectToPost the object to post * @param objectClassToReceive the object class to receive * @return the list * @throws SmartsheetException the smartsheet exception */ protected List postAndReceiveList(String path, T objectToPost, Class objectClassToReceive) throws SmartsheetException { Util.throwIfNull(path, objectToPost, objectClassToReceive); Util.throwIfEmpty(path); HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST); ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream(); this.smartsheet.getJsonSerializer().serialize(objectToPost, objectBytesStream); HttpEntity entity = new HttpEntity(); entity.setContentType(JSON_CONTENT_TYPE); entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray())); entity.setContentLength(objectBytesStream.size()); request.setEntity(entity); List obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeListResult(objectClassToReceive, response.getEntity().getContent()).getResult(); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * Post an object to Smartsheet REST API and receive a CopyOrMoveRowResult object from response. *

* Parameters: - path : the relative path of the resource collections - objectToPost : the object to post - *

* Returns: the object *

* Exceptions: * IllegalArgumentException : if any argument is null, or path is empty string * InvalidRequestException : if there is any problem with the REST API request * AuthorizationException : if there is any problem with the REST API authorization(access token) * ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * SmartsheetRestException : if there is any other REST API related error occurred during the operation * SmartsheetException : if there is any other error occurred during the operation * * @param path the path * @param objectToPost the object to post * @return the result object * @throws SmartsheetException the smartsheet exception */ protected CopyOrMoveRowResult postAndReceiveRowObject(String path, CopyOrMoveRowDirective objectToPost) throws SmartsheetException { Util.throwIfNull(path, objectToPost); Util.throwIfEmpty(path); HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.POST); ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream(); this.smartsheet.getJsonSerializer().serialize(objectToPost, objectBytesStream); HttpEntity entity = new HttpEntity(); entity.setContentType(JSON_CONTENT_TYPE); entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray())); entity.setContentLength(objectBytesStream.size()); request.setEntity(entity); CopyOrMoveRowResult obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeCopyOrMoveRow( response.getEntity().getContent()); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * Put an object to Smartsheet REST API and receive a list of objects from response. * * @param the generic type * @param the generic type * @param path the relative path of the resource collections * @param objectToPut the object to put * @param objectClassToReceive the resource object class to receive * @return the object list * @throws IllegalArgumentException : if any argument is null, or path is empty string * @throws InvalidRequestException : if there is any problem with the REST API request * @throws AuthorizationException : if there is any problem with the REST API authorization(access token) * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * @throws SmartsheetRestException : if there is any other REST API related error occurred during the operation * @throws SmartsheetException : if there is any other error occurred during the operation */ protected List putAndReceiveList(String path, T objectToPut, Class objectClassToReceive) throws SmartsheetException { Util.throwIfNull(path, objectToPut, objectClassToReceive); Util.throwIfEmpty(path); HttpRequest request = createHttpRequest(smartsheet.getBaseURI().resolve(path), HttpMethod.PUT); ByteArrayOutputStream objectBytesStream = new ByteArrayOutputStream(); this.smartsheet.getJsonSerializer().serialize(objectToPut, objectBytesStream); HttpEntity entity = new HttpEntity(); entity.setContentType(JSON_CONTENT_TYPE); entity.setContent(new ByteArrayInputStream(objectBytesStream.toByteArray())); entity.setContentLength(objectBytesStream.size()); request.setEntity(entity); List obj = null; try { HttpResponse response = this.smartsheet.getHttpClient().request(request); switch (response.getStatusCode()) { case 200: obj = this.smartsheet.getJsonSerializer().deserializeListResult( objectClassToReceive, response.getEntity().getContent()).getResult(); break; default: handleError(response); } } finally { smartsheet.getHttpClient().releaseConnection(); } return obj; } /** * Create an HttpRequest. * * @param uri the URI * @param method the HttpMethod * @return the http request */ protected HttpRequest createHttpRequest(URI uri, HttpMethod method) { HttpRequest request = new HttpRequest(); request.setUri(uri); request.setMethod(method); // Set authorization header request.setHeaders(createHeaders()); return request; } protected HttpPost createHttpPost(URI uri) { HttpPost httpPost = new HttpPost(uri); Map headers = createHeaders(); for (Map.Entry entry : headers.entrySet()) { httpPost.addHeader(entry.getKey(), entry.getValue()); } return httpPost; } /** * Attach a file */ public Attachment attachFile(String url, InputStream inputStream, String contentType, long contentLength, String attachmentName) throws SmartsheetException { Util.throwIfNull(inputStream, contentType); HttpRequest request = createHttpRequest(this.getSmartsheet().getBaseURI().resolve(url), HttpMethod.POST); request.getHeaders().put( "Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(attachmentName, StandardCharsets.UTF_8) + "\"" ); HttpEntity entity = new HttpEntity(); entity.setContentType(contentType); entity.setContent(new LengthEnforcingInputStream(inputStream, contentLength)); entity.setContentLength(contentLength); request.setEntity(entity); Attachment attachment = null; try { HttpResponse response = this.getSmartsheet().getHttpClient().request(request); switch (response.getStatusCode()) { case 200: attachment = this.getSmartsheet().getJsonSerializer().deserializeResult(Attachment.class, response.getEntity().getContent()).getResult(); break; default: handleError(response); } } finally { this.getSmartsheet().getHttpClient().releaseConnection(); } return attachment; } /** * Create a multipart upload request. * * @param url the url * @param t the object to create * @param partName the name of the part * @param inputstream the file inputstream * @param contentType the type of the file to be attached * @return the http request * @throws SmartsheetException may be thrown in the method */ public Attachment attachFile(String url, T t, String partName, InputStream inputstream, String contentType, String attachmentName) throws SmartsheetException { Util.throwIfNull(inputstream, contentType); Attachment attachment = null; final String boundary = "----" + System.currentTimeMillis(); CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost uploadFile = createHttpPost(this.getSmartsheet().getBaseURI().resolve(url)); try { uploadFile.setHeader(HEADER_CONTENT_TYPE, "multipart/form-data; boundary=" + boundary); } catch (Exception e) { throw new RuntimeException(e); } MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setBoundary(boundary); builder.addTextBody(partName, this.getSmartsheet().getJsonSerializer().serialize(t), ContentType.APPLICATION_JSON); builder.addBinaryBody("file", inputstream, ContentType.create(contentType), attachmentName); org.apache.http.HttpEntity multipart = builder.build(); uploadFile.setEntity(multipart); try { CloseableHttpResponse response = httpClient.execute(uploadFile); org.apache.http.HttpEntity responseEntity = response.getEntity(); attachment = this.getSmartsheet().getJsonSerializer().deserializeResult(Attachment.class, responseEntity.getContent()).getResult(); } catch (Exception e) { throw new RuntimeException(e); } return attachment; } /** * Handles an error HttpResponse (non-200) returned by Smartsheet REST API. * * @param response the HttpResponse * @throws SmartsheetException the smartsheet exception * @throws SmartsheetRestException : the exception corresponding to the error */ protected void handleError(HttpResponse response) throws SmartsheetException { com.smartsheet.api.models.Error error; try { error = this.smartsheet.getJsonSerializer().deserialize( com.smartsheet.api.models.Error.class, response.getEntity().getContent()); } catch (JsonParseException e) { throw new SmartsheetException(e); } catch (JsonMappingException e) { throw new SmartsheetException(e); } catch (IOException e) { throw new SmartsheetException(e); } ErrorCode code = ErrorCode.getErrorCode(response.getStatusCode()); if (code == null) { throw new SmartsheetRestException(error); } try { throw code.getException(error); } catch (IllegalArgumentException e) { throw new SmartsheetException(e); } catch (SecurityException e) { throw new SmartsheetException(e); } } /** * Gets the smartsheet. * * @return the smartsheet */ public SmartsheetImpl getSmartsheet() { return smartsheet; } /** * Get a sheet as a file. * * @param path the path * @param fileType the output file type * @param outputStream the OutputStream to which the file will be written * @throws InvalidRequestException : if there is any problem with the REST API request * @throws AuthorizationException : if there is any problem with the REST API authorization(access token) * @throws ResourceNotFoundException : if the resource can not be found * @throws ServiceUnavailableException : if the REST API service is not available (possibly due to rate limiting) * @throws SmartsheetRestException : if there is any other REST API related error occurred during the operation * @throws SmartsheetException : if there is any other error occurred during the operation */ public void getResourceAsFile(String path, String fileType, OutputStream outputStream) throws SmartsheetException { Util.throwIfNull(outputStream, fileType); HttpRequest request; request = createHttpRequest(this.getSmartsheet().getBaseURI().resolve(path), HttpMethod.GET); request.getHeaders().put("Accept", fileType); try { HttpResponse response = getSmartsheet().getHttpClient().request(request); switch (response.getStatusCode()) { case 200: try { copyStream(response.getEntity().getContent(), outputStream); } catch (IOException e) { throw new SmartsheetException(e); } break; default: handleError(response); } } finally { getSmartsheet().getHttpClient().releaseConnection(); } } /* * Copy an input stream to an output stream. * * @param input The input stream to copy. * * @param output the output stream to write to. * * @throws IOException if there is trouble reading or writing to the streams. */ /** * Copy stream. * * @param input the input * @param output the output * @throws IOException Signals that an I/O exception has occurred. * @deprecated replace with StreamUtil.copyContentIntoOutputStream() */ @Deprecated(since = "2.0.0", forRemoval = true) private static void copyStream(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[BUFFER_SIZE]; int len; while ((len = input.read(buffer)) != -1) { output.write(buffer, 0, len); } } /** * @return a map of headers to be used when making requests. */ Map createHeaders() { Map headers = new HashMap<>(); headers.put("Authorization", "Bearer " + smartsheet.getAccessToken()); headers.put(HEADER_CONTENT_TYPE, JSON_CONTENT_TYPE); // Set assumed user if (smartsheet.getAssumedUser() != null) { headers.put("Assume-User", URLEncoder.encode(smartsheet.getAssumedUser(), StandardCharsets.UTF_8)); } if (smartsheet.getChangeAgent() != null) { headers.put("Smartsheet-Change-Agent", URLEncoder.encode(smartsheet.getChangeAgent(), StandardCharsets.UTF_8)); } if (smartsheet.getUserAgent() != null) { headers.put("User-Agent", smartsheet.getUserAgent()); } return headers; } int getResponseLogLength() { // not cached to allow for it to be changed dynamically by client code return Integer.getInteger(PROPERTY_RESPONSE_LOG_CHARS, 1024); } }