This is an automated email from the ASF dual-hosted git repository. jeremyross pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push: new c2b6f494f6a CAMEL-16642: camel-salesforce: Detect SObject type (#8275) c2b6f494f6a is described below commit c2b6f494f6a89ec7ab85925ea735f570a12a64b2 Author: Jeremy Ross <jeremy.g.r...@gmail.com> AuthorDate: Mon Sep 5 14:25:07 2022 -0500 CAMEL-16642: camel-salesforce: Detect SObject type (#8275) CAMEL-16642: camel-salesforce: Detect SObject type Sniff the response to detect SObject type. Implemented for query* and apexcall operations. --- .../src/main/docs/salesforce-component.adoc | 32 +++++++++++++-------- .../salesforce/api/SalesforceException.java | 4 +++ .../internal/processor/AbstractRestProcessor.java | 32 +++++++++++++-------- .../processor/AbstractSalesforceProcessor.java | 27 ++++++++++++++---- .../CompositeSObjectCollectionsProcessor.java | 2 +- .../internal/processor/JsonRestProcessor.java | 33 +++++++++++++++++++++- .../salesforce/RestApiIntegrationTest.java | 32 +++++++++++++++++++-- 7 files changed, 129 insertions(+), 33 deletions(-) diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc b/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc index fbb608cb786..7b7e6961839 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc +++ b/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc @@ -522,15 +522,16 @@ Deletes a record in salesforce by External ID. `query` -Runs a Salesforce SOQL query. +Runs a Salesforce SOQL query. If neither `sObjectClass` nor `sObjectName` are set, Camel will attempt to determine +the correct `AbstractQueryRecordsBase` sublcass based on the response. |=== | Parameter | Type | Description | Default | Required | Body or `sObjectQuery` | `String` | SOQL query | | x | streamQueryResult | `Boolean` | If true, returns a streaming `Iterator` and transparently retrieves all pages as needed. The `sObjectClass` option must reference an `AbstractQueryRecordsBase` subclass. | false | -| `sObjectClass` | `String` | Fully qualified name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `org.my.dto.QueryRecordsAccount`| | One of sObjectClass or sObjectName is required -| `sObjectName` | `String` | Simple name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `QueryRecordsAccount`. Requires the `package` option be set.| |One of sObjectClass or sObjectName is required +| `sObjectClass` | `String` | Fully qualified name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `org.my.dto.QueryRecordsAccount`| | +| `sObjectName` | `String` | Simple name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `QueryRecordsAccount`. Requires the `package` option be set.| | |=== *Output* @@ -542,14 +543,16 @@ Type: Instance of class supplied in `sObjectClass`, or `Iterator<SomeSObject>` i `queryMore` -Retrieves more results (in case of large number of results) using result link returned from the `query` and `queryAll` operations. +Retrieves more results (in case of large number of results) using result link returned from the `query` and `queryAll` +operations. If neither `sObjectClass` nor `sObjectName` are set, Camel will attempt to determine the correct +`AbstractQueryRecordsBase` sublcass based on the response. |=== | Parameter | Type | Description | Default | Required | Body or `sObjectQuery` | `String` | `nextRecords` value. Can be found in a prior query result in the `AbstractQueryRecordsBase.nextRecordsUrl` property | | X -| `sObjectClass` | `String` | Fully qualified name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `org.my.dto.QueryRecordsAccount`| |One of sObjectClass or sObjectName is required -| `sObjectName` | `String` | Simple name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `QueryRecordsAccount`. Requires the `package` option be set. | |One of sObjectClass or sObjectName is required +| `sObjectClass` | `String` | Fully qualified name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `org.my.dto.QueryRecordsAccount`| | +| `sObjectName` | `String` | Simple name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `QueryRecordsAccount`. Requires the `package` option be set. | | |=== @@ -562,15 +565,18 @@ Type: Instance of class supplied in `sObjectClass` `queryAll` -Executes the specified SOQL query. Unlike the `query` operation , `queryAll` returns records that are deleted because of a merge or delete. It also returns information about archived task and event records. +Executes the specified SOQL query. Unlike the `query` operation , `queryAll` returns records that are deleted because +of a merge or delete. It also returns information about archived task and event records. If neither `sObjectClass` nor +`sObjectName` are set, Camel will attempt to determine the correct `AbstractQueryRecordsBase` sublcass based on the +response. |=== | Parameter | Type | Description | Default | Required | Body or `sObjectQuery` | `String` | SOQL query | | x | streamQueryResult | `Boolean` | If true, returns a streaming `Iterable` and transparently retrieves all pages as needed. The `sObjectClass` option must reference an `AbstractQueryRecordsBase` subclass. | false | -| `sObjectClass` | `String` | Fully qualified name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `org.my.dto.QueryRecordsAccount`| |One of sObjectClass or sObjectName is required -| `sObjectName` | `String` | Simple name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `QueryRecordsAccount`. Requires the `package` option be set.| |One of sObjectClass or sObjectName is required +| `sObjectClass` | `String` | Fully qualified name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `org.my.dto.QueryRecordsAccount`| | +| `sObjectName` | `String` | Simple name of class to deserialize response to. Usually a subclass of `AbstractQueryRecordsBase`, e.g. `QueryRecordsAccount`. Requires the `package` option be set.| | |=== *Output* @@ -1008,7 +1014,9 @@ salesforce:apexCall[/yourApexRestUrl][?options] You can supply the apexUrl either in the endpoint (see above), or as the `apexUrl` option as listed in the table below. In either case the Apex URL can contain placeholders in the format of `\{headerName}`. E.g., for the Apex URL `MyApexClass/\{id}`, -the value of the header named `id` will be used to replace the placeholder. +the value of the header named `id` will be used to replace the placeholder. If `rawPayload` is false and neither +`sObjectClass` nor `sObjectName` are set, Camel will attempt to determine the correct `AbstractQueryRecordsBase` +sublcass based on the response. |=== | Parameter | Type | Description| Default| Required @@ -1019,8 +1027,8 @@ is transformed into query parameters. For other HTTP methods, the body is used f | `apexMethod` | `String` | The HTTP method (e.g. `GET`, `POST`) to use. | `GET` | | `rawPayload` | `Boolean` | If true, Camel will not serialize the request or response bodies. | false | | Header: `apexQueryParam.[paramName]` | Object | Headers that override apex parameters passed in the endpoint. | | -| `sObjectName` | `String` | Name of sObject (e.g. `Merchandise__c`) used to deserialize the response | | If `rawPayload` is false and `sObjectClass` not supplied -| `sObjectClass` | `String` | Fully qualified class name used to deserialize the response | | If `rawPayload` is false and `sObjectName` not supplied +| `sObjectName` | `String` | Name of sObject (e.g. `Merchandise__c`) used to deserialize the response | | +| `sObjectClass` | `String` | Fully qualified class name used to deserialize the response | | |=== *Output* diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java index 529c094fe9b..356ee7eee7f 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceException.java @@ -31,6 +31,10 @@ public class SalesforceException extends CamelException { private final int statusCode; private final InputStream responseContent; + public SalesforceException(String message) { + this(message, null); + } + public SalesforceException(Throwable cause) { this(null, 0, null, cause); } diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java index df4b4efd642..cc6d3699c9f 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java @@ -63,6 +63,10 @@ import static org.apache.camel.component.salesforce.SalesforceEndpointConfig.STR public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor { protected static final String RESPONSE_CLASS = AbstractRestProcessor.class.getName() + ".responseClass"; + protected static final String RESPONSE_CLASS_DEFERRED = AbstractRestProcessor.class.getName() + + ".responseClassDeferred"; + protected static final String RESPONSE_CLASS_PREFIX = AbstractRestProcessor.class.getName() + + ".responseClassPrefix"; protected static final String RESPONSE_TYPE = JsonRestProcessor.class.getName() + ".responseType"; private static final Pattern URL_TEMPLATE = Pattern.compile("\\{([^\\{\\}]+)\\}"); @@ -332,7 +336,7 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor final String sObjectId = determineSObjectId(exchange); // use sObject name to load class - setResponseClass(exchange, sObjectName); + setResponseClass(exchange); // get optional field list String fieldsValue = getParameter(SOBJECT_FIELDS, exchange, IGNORE_BODY, IS_OPTIONAL); @@ -403,7 +407,7 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor } // use sObject name to load class - setResponseClass(exchange, sObjectName); + setResponseClass(exchange); final Object finalOldValue = oldValue; restClient.getSObjectWithId(sObjectName, sObjectExtIdName, sObjectExtIdValue, determineHeaders(exchange), @@ -492,10 +496,10 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor private void processQuery(final Exchange exchange, final AsyncCallback callback) throws SalesforceException { final String sObjectQuery = getParameter(SOBJECT_QUERY, exchange, USE_BODY, NOT_OPTIONAL); final boolean streamQueryResults = getParameter(STREAM_QUERY_RESULT, exchange, IGNORE_BODY, IS_OPTIONAL, Boolean.class); - final String sObjectName = getParameter(SOBJECT_NAME, exchange, IGNORE_BODY, IS_OPTIONAL); // use custom response class property - setResponseClass(exchange, sObjectName); + setResponseClass(exchange); + exchange.setProperty(RESPONSE_CLASS_PREFIX, "QueryRecords"); if (streamQueryResults) { restClient.query(sObjectQuery, determineHeaders(exchange), processWithStreamResultCallback(exchange, callback)); @@ -507,10 +511,10 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor private void processQueryMore(final Exchange exchange, final AsyncCallback callback) throws SalesforceException { // reuse SOBJECT_QUERY parameter name for nextRecordsUrl final String nextRecordsUrl = getParameter(SOBJECT_QUERY, exchange, USE_BODY, NOT_OPTIONAL); - final String sObjectName = getParameter(SOBJECT_NAME, exchange, IGNORE_BODY, IS_OPTIONAL); // use custom response class property - setResponseClass(exchange, sObjectName); + setResponseClass(exchange); + exchange.setProperty(RESPONSE_CLASS_PREFIX, "QueryRecords"); restClient.queryMore(nextRecordsUrl, determineHeaders(exchange), processWithResponseCallback(exchange, callback)); } @@ -518,10 +522,10 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor private void processQueryAll(final Exchange exchange, final AsyncCallback callback) throws SalesforceException { final String sObjectQuery = getParameter(SOBJECT_QUERY, exchange, USE_BODY, NOT_OPTIONAL); final boolean streamQueryResults = getParameter(STREAM_QUERY_RESULT, exchange, IGNORE_BODY, IS_OPTIONAL, Boolean.class); - final String sObjectName = getParameter(SOBJECT_NAME, exchange, IGNORE_BODY, IS_OPTIONAL); // use custom response class property - setResponseClass(exchange, sObjectName); + setResponseClass(exchange); + exchange.setProperty(RESPONSE_CLASS_PREFIX, "QueryRecords"); if (streamQueryResults) { restClient.queryAll(sObjectQuery, determineHeaders(exchange), processWithStreamResultCallback(exchange, callback)); @@ -549,7 +553,7 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor final Map<String, Object> queryParams = getQueryParams(exchange); // set response class - setResponseClass(exchange, getParameter(SOBJECT_NAME, exchange, IGNORE_BODY, IS_OPTIONAL)); + setResponseClass(exchange); // set request stream final Object requestBody = exchange.getIn().getBody(); @@ -731,15 +735,19 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor */ protected abstract InputStream getRequestStream(Message in, Object object) throws SalesforceException; - private void setResponseClass(Exchange exchange, String sObjectName) throws SalesforceException { + protected void setResponseClass(Exchange exchange) throws SalesforceException { // nothing to do if using rawPayload if (rawPayload) { return; } - Class<?> sObjectClass = getSObjectClass(sObjectName, exchange); - exchange.setProperty(RESPONSE_CLASS, sObjectClass); + Class<?> sObjectClass = getSObjectClass(exchange); + if (sObjectClass != null) { + exchange.setProperty(RESPONSE_CLASS, sObjectClass); + } else { + exchange.setProperty(RESPONSE_CLASS_DEFERRED, true); + } } final ResponseCallback processWithResponseCallback(final Exchange exchange, final AsyncCallback callback) { diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractSalesforceProcessor.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractSalesforceProcessor.java index d028ed62c91..773733222ec 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractSalesforceProcessor.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractSalesforceProcessor.java @@ -175,18 +175,35 @@ public abstract class AbstractSalesforceProcessor extends ServiceSupport impleme } } - protected Class<?> getSObjectClass(String sObjectName, Exchange exchange) throws SalesforceException { + protected Class<?> getSObjectClass(Exchange exchange) throws SalesforceException { + final String sObjectName = getParameter(SalesforceEndpointConfig.SOBJECT_NAME, exchange, IGNORE_BODY, IS_OPTIONAL); + final String className = getParameter(SalesforceEndpointConfig.SOBJECT_CLASS, exchange, IGNORE_BODY, IS_OPTIONAL); + return getSObjectClass(sObjectName, className); + } + + /** + * + * @param sObjectName if provided, will attempt to look up class by simple name + * @param className if provided, will attempt to look up class by fully qualified name + * @return Class, if found. + * @throws SalesforceException if unable to find class by whichever parameter was non-null + */ + protected Class<?> getSObjectClass(String sObjectName, String className) throws SalesforceException { Class<?> sObjectClass = null; if (sObjectName != null) { sObjectClass = classMap.get(sObjectName); + if (sObjectClass == null) { + throw new SalesforceException( + String.format("SObject class not found for sObjectName: %s", sObjectName)); + } } - if (sObjectClass == null) { - final String className = getParameter(SalesforceEndpointConfig.SOBJECT_CLASS, exchange, IGNORE_BODY, NOT_OPTIONAL); + if (className != null) { try { - sObjectClass = endpoint.getComponent().getCamelContext().getClassResolver().resolveMandatoryClass(className); + sObjectClass + = endpoint.getComponent().getCamelContext().getClassResolver().resolveMandatoryClass(className); } catch (ClassNotFoundException e) { throw new SalesforceException( - String.format("SObject class not found %s or by sObjectName %s", className, sObjectName), e); + String.format("SObject class not found %s", className), e); } } return sObjectClass; diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/CompositeSObjectCollectionsProcessor.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/CompositeSObjectCollectionsProcessor.java index 03d6a20d111..979c2d4708b 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/CompositeSObjectCollectionsProcessor.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/CompositeSObjectCollectionsProcessor.java @@ -98,7 +98,7 @@ public class CompositeSObjectCollectionsProcessor extends AbstractSalesforceProc String sObjectName = getParameter(SalesforceEndpointConfig.SOBJECT_NAME, exchange, IGNORE_BODY, IS_OPTIONAL); // gets class by sObjectName if not null, otherwise tries the SOBJECT_CLASS option - Class<?> sObjectClass = getSObjectClass(sObjectName, exchange); + Class<?> sObjectClass = getSObjectClass(exchange); RetrieveSObjectCollectionsDto request = new RetrieveSObjectCollectionsDto(ids, fields); compositeClient.submitRetrieveCompositeCollections(request, determineHeaders(exchange), diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java index fd8bf7094f1..b3e4d02c1bc 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java @@ -23,6 +23,9 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Map; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.AsyncCallback; @@ -196,6 +199,9 @@ public class JsonRestProcessor extends AbstractRestProcessor { // do we need to un-marshal a response final Object response; Class<?> responseClass = exchange.getProperty(RESPONSE_CLASS, Class.class); + if (responseClass == null && exchange.getProperty(RESPONSE_CLASS_DEFERRED, false, Boolean.class)) { + responseClass = detectResponseClass(exchange, responseEntity); + } if (!rawPayload && responseClass != null) { response = objectMapper.readValue(responseEntity, responseClass); } else { @@ -203,7 +209,7 @@ public class JsonRestProcessor extends AbstractRestProcessor { if (!rawPayload && responseType != null) { response = objectMapper.readValue(responseEntity, responseType); } else { - // return the response as a stream, for getBlobField + // return the response as a stream, for getBlobField and rawPayload response = responseEntity; } } @@ -231,6 +237,29 @@ public class JsonRestProcessor extends AbstractRestProcessor { } + private Class<?> detectResponseClass(Exchange exchange, InputStream responseEntity) throws IOException { + Class<?> responseClass; + try { + final JsonParser parser = new JsonFactory().createParser(responseEntity); + String type = null; + while (parser.nextToken() != JsonToken.END_OBJECT) { + String propName = parser.getCurrentName(); + if ("type".equals(propName)) { + parser.nextToken(); + type = parser.getText(); + break; + } + } + String prefix = exchange.getProperty(RESPONSE_CLASS_PREFIX, "", String.class); + responseClass = getSObjectClass(prefix + type, null); + } catch (IOException | SalesforceException exc) { + throw new RuntimeException(exc); + } finally { + responseEntity.reset(); + } + return responseClass; + } + @Override protected void processStreamResultResponse( Exchange exchange, InputStream responseEntity, Map<String, String> headers, SalesforceException ex, @@ -265,6 +294,8 @@ public class JsonRestProcessor extends AbstractRestProcessor { exchange.setException(new SalesforceException(msg, e)); } finally { exchange.removeProperty(RESPONSE_CLASS); + exchange.removeProperty(RESPONSE_CLASS_DEFERRED); + exchange.removeProperty(RESPONSE_CLASS_PREFIX); exchange.removeProperty(RESPONSE_TYPE); try { diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java index 6ea82df1142..0966e5ee092 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java +++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java @@ -227,6 +227,15 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { assertEquals("test response", IOUtils.toString(exception.getResponseContent(), StandardCharsets.UTF_8)); } + @Test + public void testApexCallDetectResponseType() throws Exception { + // request merchandise with id in URI template + Merchandise__c merchandise + = template().requestBodyAndHeader("direct:apexCallGetDetectResponseType", null, "id", merchandiseId, + Merchandise__c.class); + assertNotNull(merchandise); + } + @Test public void returnsHttpResponseStatusAndText() { Exchange exchange = new DefaultExchange(context); @@ -512,6 +521,14 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { assertNotNull(lineItem.getRecordType()); } + @Test + public void testQueryDetectResponseClass() throws Exception { + createLineItem(); + final QueryRecordsLine_Item__c queryRecords + = template().requestBody("direct:queryDetectResponseClass", null, QueryRecordsLine_Item__c.class); + assertNotNull(queryRecords); + } + @Test public void testQueryWithSObjectName() throws Exception { createLineItem(); @@ -823,6 +840,13 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { from("direct:getBlobField") .to("salesforce:getBlobField?sObjectName=Document&sObjectBlobFieldName=Body"); + // testQuery + from("direct:queryDetectResponseClass") + .to("salesforce:query?sObjectQuery=SELECT Id, name, Typeof Owner WHEN User Then Username End, recordTypeId, RecordType.Name " + + "from Line_Item__c " + + "ORDER BY CreatedDate DESC " + + "LIMIT 1"); + // testQuery from("direct:query") .to("salesforce:query?sObjectQuery=SELECT Id, name, Typeof Owner WHEN User Then Username End, recordTypeId, RecordType.Name " @@ -833,8 +857,8 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { // testQuery from("direct:queryWithSObjectName") - .to("salesforce:query?sObjectQuery=SELECT Id, name, Typeof Owner WHEN User Then Username End, recordTypeId, RecordType.Name from Line_Item__c&sObjectName=" - + "QueryRecordsLine_Item__c"); + .to("salesforce:query?sObjectQuery=SELECT Id, name, Typeof Owner WHEN User Then Username End, recordTypeId, RecordType.Name from Line_Item__c" + + "&sObjectName=QueryRecordsLine_Item__c"); // testQuery from("direct:queryStreamResult") @@ -885,6 +909,10 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { from("direct:apexCallGet") .to("salesforce:apexCall?apexMethod=GET&apexUrl=Merchandise/{id}&sObjectName=Merchandise__c"); + // testApexCall + from("direct:apexCallGetDetectResponseType") + .to("salesforce:apexCall?apexMethod=GET&apexUrl=Merchandise/{id}"); + from("direct:apexCallGetWithId") .to("salesforce:apexCall/Merchandise/?apexMethod=GET&id=dummyId" + "&sObjectClass=" + Merchandise__c.class.getName());