RestRequest.java
/*
* Copyright (c) 2011-present, salesforce.com, inc.
* All rights reserved.
* Redistribution and use of this software in source and binary forms, with or
* without modification, are permitted provided that the following conditions
* are met:
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of salesforce.com, inc. nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission of salesforce.com, inc.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.salesforce.androidsdk.rest;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import okhttp3.MediaType;
import okhttp3.RequestBody;
/**
* RestRequest: Class to represent any REST request.
*
* The class offers factory methods to build RestRequest objects for all REST API actions:
* <ul>
* <li> versions</li>
* <li> resources</li>
* <li> describeGlobal</li>
* <li> metadata</li>
* <li> describe</li>
* <li> create</li>
* <li> retrieve</li>
* <li> update</li>
* <li> upsert</li>
* <li> delete</li>
* <li> searchScopeAndOrder</li>
* <li> searchResultLayout</li>
* </ul>
*
* It also has constructors to build any arbitrary request.
*
*/
public class RestRequest {
/**
* application/json media type
*/
public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
/**
* utf_8 charset
*/
public static final String UTF_8 = StandardCharsets.UTF_8.name();
/**
* Enumeration for all HTTP methods.
*
*/
public enum RestMethod {
GET, POST, PUT, DELETE, HEAD, PATCH
}
/**
* Enumeration for all REST API actions.
*/
private enum RestAction {
VERSIONS("/services/data/"),
RESOURCES("/services/data/%s/"),
DESCRIBE_GLOBAL("/services/data/%s/sobjects/"),
METADATA("/services/data/%s/sobjects/%s/"),
DESCRIBE("/services/data/%s/sobjects/%s/describe/"),
CREATE("/services/data/%s/sobjects/%s"),
RETRIEVE("/services/data/%s/sobjects/%s/%s"),
UPSERT("/services/data/%s/sobjects/%s/%s/%s"),
UPDATE("/services/data/%s/sobjects/%s/%s"),
DELETE("/services/data/%s/sobjects/%s/%s"),
QUERY("/services/data/%s/query"),
SEARCH("/services/data/%s/search"),
SEARCH_SCOPE_AND_ORDER("/services/data/%s/search/scopeOrder"),
SEARCH_RESULT_LAYOUT("/services/data/%s/search/layout");
private final String pathTemplate;
RestAction(String uriTemplate) {
this.pathTemplate = uriTemplate;
}
public String getPath(Object... args) {
return String.format(pathTemplate, args);
}
}
private final RestMethod method;
private final String path;
private final RequestBody requestBody;
private final Map<String, String> additionalHttpHeaders;
/**
* Generic constructor for arbitrary requests.
*
* @param method the HTTP method for the request (GET/POST/DELETE etc)
* @param path the URI path, this will automatically be resolved against the users current instance host.
* @param requestBody the request body if there is one, can be null.
*/
public RestRequest(RestMethod method, String path, RequestBody requestBody) {
this(method, path, requestBody, null);
}
public RestRequest(RestMethod method, String path, RequestBody requestBody, Map<String, String> additionalHttpHeaders) {
this.method = method;
this.path = path;
this.requestBody = requestBody;
this.additionalHttpHeaders = additionalHttpHeaders;
}
/**
* @return HTTP method of the request.
*/
public RestMethod getMethod() {
return method;
}
/**
* @return Path of the request.
*/
public String getPath() {
return path;
}
/**
* @return request RequestBody
*/
public RequestBody getRequestBody() {
return requestBody;
}
/**
* @return addition http headers
*/
public Map<String, String> getAdditionalHttpHeaders() {
return additionalHttpHeaders;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append(method).append(" ").append(path);
return sb.toString();
}
/**
* Request to get summary information about each Salesforce.com version currently available.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_versions.htm
*
* @return a JsonNode
*/
public static RestRequest getRequestForVersions() {
return new RestRequest(RestMethod.GET, RestAction.VERSIONS.getPath(), null);
}
/**
* Request to list available resources for the specified API version, including resource name and URI.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_discoveryresource.htm
*
* @param apiVersion
* @return a RestRequest
*/
public static RestRequest getRequestForResources(String apiVersion) {
return new RestRequest(RestMethod.GET, RestAction.RESOURCES.getPath(apiVersion), null);
}
/**
* Request to list the available objects and their metadata for your organization's data.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_describeGlobal.htm
*
* @param apiVersion
* @return a RestRequest
*/
public static RestRequest getRequestForDescribeGlobal(String apiVersion) {
return new RestRequest(RestMethod.GET, RestAction.DESCRIBE_GLOBAL.getPath(apiVersion), null);
}
/**
* Request to describe the individual metadata for the specified object.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_basic_info.htm
*
* @param apiVersion
* @param objectType
* @return a RestRequest
* @throws IOException
*/
public static RestRequest getRequestForMetadata(String apiVersion, String objectType) {
return new RestRequest(RestMethod.GET, RestAction.METADATA.getPath(apiVersion, objectType), null);
}
/**
* Request to completely describe the individual metadata at all levels for the specified object.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_describe.htm
*
* @param apiVersion
* @param objectType
* @return a RestRequest
*/
public static RestRequest getRequestForDescribe(String apiVersion, String objectType) {
return new RestRequest(RestMethod.GET, RestAction.DESCRIBE.getPath(apiVersion, objectType), null);
}
/**
* Request to create a record.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_retrieve.htm
*
* @param apiVersion
* @param objectType
* @param fields
* @return a RestRequest
* @throws IOException
* @throws UnsupportedEncodingException
*/
public static RestRequest getRequestForCreate(String apiVersion, String objectType, Map<String, Object> fields) throws IOException {
RequestBody fieldsData = prepareFieldsData(fields);
return new RestRequest(RestMethod.POST, RestAction.CREATE.getPath(apiVersion, objectType), fieldsData);
}
/**
* Request to retrieve a record by object id.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_retrieve.htm
*
* @param apiVersion
* @param objectType
* @param objectId
* @param fieldList
* @return a RestRequest
* @throws UnsupportedEncodingException
*/
public static RestRequest getRequestForRetrieve(String apiVersion, String objectType, String objectId, List<String> fieldList) throws UnsupportedEncodingException {
StringBuilder path = new StringBuilder(RestAction.RETRIEVE.getPath(apiVersion, objectType, objectId));
if (fieldList != null && fieldList.size() > 0) {
path.append("?fields=");
path.append(URLEncoder.encode(toCsv(fieldList).toString(), UTF_8));
}
return new RestRequest(RestMethod.GET, path.toString(), null);
}
private static StringBuilder toCsv(List<String> fieldList) {
StringBuilder fieldsCsv = new StringBuilder();
for (int i=0; i<fieldList.size(); i++) {
fieldsCsv.append(fieldList.get(i));
if (i<fieldList.size() - 1) {
fieldsCsv.append(",");
}
}
return fieldsCsv;
}
/**
* Request to update a record.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_retrieve.htm
*
* @param apiVersion
* @param objectType
* @param objectId
* @param fields
* @return a RestRequest
* @throws IOException
*/
public static RestRequest getRequestForUpdate(String apiVersion, String objectType, String objectId, Map<String, Object> fields) throws IOException {
RequestBody fieldsData = prepareFieldsData(fields);
return new RestRequest(RestMethod.PATCH, RestAction.UPDATE.getPath(apiVersion, objectType, objectId), fieldsData);
}
/**
* Request to upsert (update or insert) a record.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_upsert.htm
*
* @param apiVersion
* @param objectType
* @param externalIdField
* @param externalId
* @param fields
* @return a RestRequest
* @throws IOException
*/
public static RestRequest getRequestForUpsert(String apiVersion, String objectType, String externalIdField, String externalId, Map<String, Object> fields) throws IOException {
RequestBody fieldsData = prepareFieldsData(fields);
return new RestRequest(RestMethod.PATCH, RestAction.UPSERT.getPath(apiVersion, objectType, externalIdField, externalId), fieldsData);
}
/**
* Request to delete a record.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_sobject_retrieve.htm
*
* @param apiVersion
* @param objectType
* @param objectId
* @return a RestRequest
*/
public static RestRequest getRequestForDelete(String apiVersion, String objectType, String objectId) {
return new RestRequest(RestMethod.DELETE, RestAction.DELETE.getPath(apiVersion, objectType, objectId), null);
}
/**
* Request to execute the specified SOSL search.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_search.htm
*
* @param apiVersion
* @param q
* @return a RestRequest
* @throws UnsupportedEncodingException
*/
public static RestRequest getRequestForSearch(String apiVersion, String q) throws UnsupportedEncodingException {
StringBuilder path = new StringBuilder(RestAction.SEARCH.getPath(apiVersion));
path.append("?q=");
path.append(URLEncoder.encode(q, UTF_8));
return new RestRequest(RestMethod.GET, path.toString(), null);
}
/**
* Request to execute the specified SOQL search.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_query.htm
*
* @param apiVersion
* @param q
* @return a RestRequest
* @throws UnsupportedEncodingException
*/
public static RestRequest getRequestForQuery(String apiVersion, String q) throws UnsupportedEncodingException {
StringBuilder path = new StringBuilder(RestAction.QUERY.getPath(apiVersion));
path.append("?q=");
path.append(URLEncoder.encode(q, UTF_8));
return new RestRequest(RestMethod.GET, path.toString(), null);
}
/**
* Request to get search scope and order.
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_search_scope_order.htm
*
* @param apiVersion
* @return a RestRequest
* @throws UnsupportedEncodingException
*/
public static RestRequest getRequestForSearchScopeAndOrder(String apiVersion) throws UnsupportedEncodingException {
StringBuilder path = new StringBuilder(RestAction.SEARCH_SCOPE_AND_ORDER.getPath(apiVersion));
return new RestRequest(RestMethod.GET, path.toString(), null);
}
/**
* Request to get search result layouts
* See http://www.salesforce.com/us/developer/docs/api_rest/Content/resources_search_layouts.htm
*
* @param apiVersion
* @param objectList
* @return a RestRequest
* @throws UnsupportedEncodingException
*/
public static RestRequest getRequestForSearchResultLayout(String apiVersion, List<String> objectList) throws UnsupportedEncodingException {
StringBuilder path = new StringBuilder(RestAction.SEARCH_RESULT_LAYOUT.getPath(apiVersion));
path.append("?q=");
path.append(URLEncoder.encode(toCsv(objectList).toString(), UTF_8));
return new RestRequest(RestMethod.GET, path.toString(), null);
}
/**
* Jsonize map and create a RequestBody out of it
* @param fields
* @return
* @throws UnsupportedEncodingException
*/
private static RequestBody prepareFieldsData(Map<String, Object> fields)
throws UnsupportedEncodingException {
if (fields == null) {
return null;
}
else {
return RequestBody.create(MEDIA_TYPE_JSON, new JSONObject(fields).toString());
}
}
}