Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id E598F200C44 for ; Mon, 27 Mar 2017 19:27:35 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id E456E160B85; Mon, 27 Mar 2017 17:27:35 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 676FD160B7B for ; Mon, 27 Mar 2017 19:27:34 +0200 (CEST) Received: (qmail 2776 invoked by uid 500); 27 Mar 2017 17:27:33 -0000 Mailing-List: contact commits-help@camel.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@camel.apache.org Delivered-To: mailing list commits@camel.apache.org Received: (qmail 2767 invoked by uid 99); 27 Mar 2017 17:27:33 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 27 Mar 2017 17:27:33 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 7C675E00FF; Mon, 27 Mar 2017 17:27:33 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: zregvart@apache.org To: commits@camel.apache.org Date: Mon, 27 Mar 2017 17:27:36 -0000 Message-Id: <15d4d87c09a5435c8c463d635a8ced49@git.apache.org> In-Reply-To: <709569e583204cb787c9f9cdd550e847@git.apache.org> References: <709569e583204cb787c9f9cdd550e847@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [4/4] camel git commit: CAMEL-10846 Handle 404 situations more gracefully archived-at: Mon, 27 Mar 2017 17:27:36 -0000 CAMEL-10846 Handle 404 situations more gracefully Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/87dc6fc2 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/87dc6fc2 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/87dc6fc2 Branch: refs/heads/master Commit: 87dc6fc295c697d3d0c35dd37aa9742264863017 Parents: 285bea2 Author: Zoran Regvart Authored: Mon Mar 27 19:02:38 2017 +0200 Committer: Zoran Regvart Committed: Mon Mar 27 19:25:03 2017 +0200 ---------------------------------------------------------------------- .../src/main/docs/salesforce-component.adoc | 3 +- .../component/salesforce/NotFoundBehaviour.java | 21 +++++++ .../salesforce/SalesforceEndpointConfig.java | 21 +++++++ .../salesforce/api/NoSuchSObjectException.java | 29 +++++++++ .../api/SalesforceMultipleChoicesException.java | 6 -- .../internal/client/AbstractClientBase.java | 33 ++++++++--- .../client/DefaultAnalyticsApiClient.java | 10 +++- .../client/DefaultCompositeApiClient.java | 31 +++++++--- .../internal/client/DefaultRestClient.java | 19 +++--- .../processor/AbstractRestProcessor.java | 13 +++- .../internal/processor/JsonRestProcessor.java | 23 +++++--- .../internal/processor/XmlRestProcessor.java | 23 ++++---- .../salesforce/NotFoundIntegrationTest.java | 62 ++++++++++++++++++++ .../salesforce/RestApiIntegrationTest.java | 34 +++++------ .../SalesforceComponentConfiguration.java | 16 +++++ 15 files changed, 274 insertions(+), 70 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/docs/salesforce-component.adoc ---------------------------------------------------------------------- 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 322950f..5c856ee 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 @@ -519,7 +519,7 @@ with the following path and query parameters: | **topicName** | The name of the topic to use | | String |======================================================================= -#### Query Parameters (41 parameters): +#### Query Parameters (42 parameters): [width="100%",cols="2,5,^1,2",options="header"] |======================================================================= @@ -540,6 +540,7 @@ with the following path and query parameters: | **jobId** (common) | Bulk API Job ID | | String | **limit** (common) | Limit on number of returned records. Applicable to some of the API check the Salesforce documentation. | | Integer | **maxBackoff** (common) | Maximum backoff interval for Streaming connection restart attempts for failures beyond CometD auto-reconnect. | | long +| **notFoundBehaviour** (common) | Sets the behaviour of 404 not found status received from Salesforce API. Should the body be set to NULL link NotFoundBehaviourNULL or should a exception be signaled on the exchange link NotFoundBehaviourEXCEPTION - the default. | | NotFoundBehaviour | **notifyForFields** (common) | Notify for fields options are ALL REFERENCED SELECT WHERE | | NotifyForFieldsEnum | **notifyForOperationCreate** (common) | Notify for create operation defaults to false (API version = 29.0) | | Boolean | **notifyForOperationDelete** (common) | Notify for delete operation defaults to false (API version = 29.0) | | Boolean http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/NotFoundBehaviour.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/NotFoundBehaviour.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/NotFoundBehaviour.java new file mode 100644 index 0000000..8f7feec --- /dev/null +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/NotFoundBehaviour.java @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.salesforce; + +public enum NotFoundBehaviour { + EXCEPTION, NULL +} http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java index f035449..5363c0c 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/SalesforceEndpointConfig.java @@ -91,6 +91,8 @@ public class SalesforceEndpointConfig implements Cloneable { public static final long DEFAULT_BACKOFF_INCREMENT = 1000L; public static final long DEFAULT_MAX_BACKOFF = 30000L; + public static final String NOT_FOUND_BEHAVIOUR = "notFoundBehaviour"; + // general properties @UriParam private String apiVersion = DEFAULT_VERSION; @@ -187,6 +189,9 @@ public class SalesforceEndpointConfig implements Cloneable { @UriParam private Integer limit; + @UriParam + private NotFoundBehaviour notFoundBehaviour = NotFoundBehaviour.EXCEPTION; + public SalesforceEndpointConfig copy() { try { final SalesforceEndpointConfig copy = (SalesforceEndpointConfig) super.clone(); @@ -614,6 +619,8 @@ public class SalesforceEndpointConfig implements Cloneable { valueMap.put(DEFAULT_REPLAY_ID, defaultReplayId); valueMap.put(INITIAL_REPLAY_ID_MAP, initialReplayIdMap); + valueMap.put(NOT_FOUND_BEHAVIOUR, notFoundBehaviour); + return Collections.unmodifiableMap(valueMap); } @@ -816,4 +823,18 @@ public class SalesforceEndpointConfig implements Cloneable { approval.setSkipEntryCriteria(skipEntryCriteria); } + + public NotFoundBehaviour getNotFoundBehaviour() { + return notFoundBehaviour; + } + + /** + * Sets the behaviour of 404 not found status received from Salesforce API. + * Should the body be set to NULL {@link NotFoundBehaviour#NULL} or should a + * exception be signaled on the exchange {@link NotFoundBehaviour#EXCEPTION} + * - the default. + */ + public void setNotFoundBehaviour(final NotFoundBehaviour notFoundBehaviour) { + this.notFoundBehaviour = notFoundBehaviour; + } } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/NoSuchSObjectException.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/NoSuchSObjectException.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/NoSuchSObjectException.java new file mode 100644 index 0000000..2096569 --- /dev/null +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/NoSuchSObjectException.java @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.salesforce.api; + +import java.util.List; + +import org.apache.camel.component.salesforce.api.dto.RestError; + +public final class NoSuchSObjectException extends SalesforceException { + + public NoSuchSObjectException(final List restErrors) { + super(restErrors, 404); + } + +} http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceMultipleChoicesException.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceMultipleChoicesException.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceMultipleChoicesException.java index 8330e5e..b6614ef 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceMultipleChoicesException.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/api/SalesforceMultipleChoicesException.java @@ -32,10 +32,4 @@ public class SalesforceMultipleChoicesException extends SalesforceException { return choices; } - @Override - public void appendFields(StringBuilder builder) { - super.appendFields(builder); - builder.append(",choices="); - builder.append(choices.toString()); - } } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/AbstractClientBase.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/AbstractClientBase.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/AbstractClientBase.java index 10799f6..af96dcd 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/AbstractClientBase.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/AbstractClientBase.java @@ -16,16 +16,26 @@ */ package org.apache.camel.component.salesforce.internal.client; +import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.thoughtworks.xstream.XStream; + import org.apache.camel.Service; import org.apache.camel.component.salesforce.SalesforceHttpClient; import org.apache.camel.component.salesforce.api.SalesforceException; +import org.apache.camel.component.salesforce.api.TypeReferences; +import org.apache.camel.component.salesforce.api.dto.RestError; +import org.apache.camel.component.salesforce.internal.PayloadFormat; import org.apache.camel.component.salesforce.internal.SalesforceSession; +import org.apache.camel.component.salesforce.internal.dto.RestErrors; import org.eclipse.jetty.client.HttpContentResponse; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.ContentResponse; @@ -167,16 +177,11 @@ public abstract class AbstractClientBase implements SalesforceSession.Salesforce } } else if (status < HttpStatus.OK_200 || status >= HttpStatus.MULTIPLE_CHOICES_300) { - // Salesforce HTTP failure! - request = (SalesforceHttpRequest) result.getRequest(); - final String msg = String.format("Error {%s:%s} executing {%s:%s}", - status, response.getReason(), request.getMethod(), request.getURI()); - final SalesforceException cause = createRestException(response, getContentAsInputStream()); + final SalesforceException exception = createRestException(response, getContentAsInputStream()); // for APIs that return body on status 400, such as Composite API we need content as well - callback.onResponse(getContentAsInputStream(), new SalesforceException(msg, response.getStatus(), cause)); - + callback.onResponse(getContentAsInputStream(), exception); } else { // Success!!! @@ -203,6 +208,20 @@ public abstract class AbstractClientBase implements SalesforceSession.Salesforce this.instanceUrl = instanceUrl; } + final List readErrorsFrom(final InputStream responseContent, final PayloadFormat format, + final ObjectMapper objectMapper, final XStream xStream) + throws IOException, JsonParseException, JsonMappingException { + final List restErrors; + if (PayloadFormat.JSON.equals(format)) { + restErrors = objectMapper.readValue(responseContent, TypeReferences.REST_ERROR_LIST_TYPE); + } else { + RestErrors errors = new RestErrors(); + xStream.fromXML(responseContent, errors); + restErrors = errors.getErrors(); + } + return restErrors; + } + protected abstract void setAccessToken(Request request); protected abstract SalesforceException createRestException(Response response, InputStream responseContent); http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultAnalyticsApiClient.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultAnalyticsApiClient.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultAnalyticsApiClient.java index 96396f8..daf624b 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultAnalyticsApiClient.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultAnalyticsApiClient.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.component.salesforce.SalesforceHttpClient; +import org.apache.camel.component.salesforce.api.NoSuchSObjectException; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.TypeReferences; import org.apache.camel.component.salesforce.api.dto.RestError; @@ -36,12 +37,14 @@ import org.apache.camel.component.salesforce.api.dto.analytics.reports.ReportIns import org.apache.camel.component.salesforce.api.dto.analytics.reports.ReportMetadata; import org.apache.camel.component.salesforce.api.dto.analytics.reports.SyncReportResults; import org.apache.camel.component.salesforce.api.utils.JsonUtils; +import org.apache.camel.component.salesforce.internal.PayloadFormat; import org.apache.camel.component.salesforce.internal.SalesforceSession; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.StringUtil; /** @@ -254,7 +257,12 @@ public class DefaultAnalyticsApiClient extends AbstractClientBase implements Ana try { if (responseContent != null) { // unmarshal RestError - final List errors = objectMapper.readValue(responseContent, TypeReferences.REST_ERROR_LIST_TYPE); + final List errors = readErrorsFrom(responseContent, PayloadFormat.JSON, objectMapper, null); + + if (statusCode == HttpStatus.NOT_FOUND_404) { + return new NoSuchSObjectException(errors); + } + return new SalesforceException(errors, statusCode); } } catch (UnsupportedEncodingException e) { http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultCompositeApiClient.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultCompositeApiClient.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultCompositeApiClient.java index cd85671..1945064 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultCompositeApiClient.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultCompositeApiClient.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -41,8 +42,10 @@ import com.thoughtworks.xstream.io.xml.XppDriver; import org.apache.camel.component.salesforce.SalesforceEndpointConfig; import org.apache.camel.component.salesforce.SalesforceHttpClient; +import org.apache.camel.component.salesforce.api.NoSuchSObjectException; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.dto.AnnotationFieldKeySorter; +import org.apache.camel.component.salesforce.api.dto.RestError; import org.apache.camel.component.salesforce.api.dto.composite.SObjectBatch; import org.apache.camel.component.salesforce.api.dto.composite.SObjectBatchResponse; import org.apache.camel.component.salesforce.api.dto.composite.SObjectTree; @@ -59,6 +62,7 @@ import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.util.InputStreamContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.StringUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,8 +85,8 @@ public class DefaultCompositeApiClient extends AbstractClientBase implements Com private final XStream xStream; public DefaultCompositeApiClient(final SalesforceEndpointConfig configuration, final PayloadFormat format, - final String version, final SalesforceSession session, final SalesforceHttpClient httpClient) - throws SalesforceException { + final String version, final SalesforceSession session, final SalesforceHttpClient httpClient) + throws SalesforceException { super(version, session, httpClient); this.format = format; @@ -120,7 +124,7 @@ public class DefaultCompositeApiClient extends AbstractClientBase implements Com @Override public void submitCompositeBatch(final SObjectBatch batch, final ResponseCallback callback) - throws SalesforceException { + throws SalesforceException { checkCompositeBatchVersion(version, batch.getVersion()); final String url = versionUrl() + "composite/batch"; @@ -136,7 +140,7 @@ public class DefaultCompositeApiClient extends AbstractClientBase implements Com @Override public void submitCompositeTree(final SObjectTree tree, final ResponseCallback callback) - throws SalesforceException { + throws SalesforceException { final String url = versionUrl() + "composite/tree/" + tree.getObjectType(); final Request post = createRequest(HttpMethod.POST, url); @@ -151,10 +155,8 @@ public class DefaultCompositeApiClient extends AbstractClientBase implements Com static void checkCompositeBatchVersion(final String configuredVersion, final Version batchVersion) throws SalesforceException { if (Version.create(configuredVersion).compareTo(batchVersion) < 0) { - throw new SalesforceException("Component is configured with Salesforce API version " - + configuredVersion - + ", but the payload of the Composite API batch operation requires at least " - + batchVersion, 0); + throw new SalesforceException("Component is configured with Salesforce API version " + configuredVersion + + ", but the payload of the Composite API batch operation requires at least " + batchVersion, 0); } } @@ -263,8 +265,19 @@ public class DefaultCompositeApiClient extends AbstractClientBase implements Com @Override protected SalesforceException createRestException(final Response response, final InputStream responseContent) { - final String reason = response.getReason(); + final List errors; + try { + errors = readErrorsFrom(responseContent, format, mapper, xStream); + } catch (IOException e) { + return new SalesforceException("Unable to read error response", e); + } + final int status = response.getStatus(); + if (status == HttpStatus.NOT_FOUND_404) { + return new NoSuchSObjectException(errors); + } + + final String reason = response.getReason(); return new SalesforceException("Unexpected error: " + reason, status); } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultRestClient.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultRestClient.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultRestClient.java index 9e190be..e8e38e0 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultRestClient.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/client/DefaultRestClient.java @@ -25,10 +25,14 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.thoughtworks.xstream.XStream; +import org.apache.camel.component.salesforce.NotFoundBehaviour; import org.apache.camel.component.salesforce.SalesforceHttpClient; +import org.apache.camel.component.salesforce.api.NoSuchSObjectException; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.SalesforceMultipleChoicesException; import org.apache.camel.component.salesforce.api.TypeReferences; @@ -59,8 +63,8 @@ public class DefaultRestClient extends AbstractClientBase implements RestClient private ObjectMapper objectMapper; private XStream xStream; - public DefaultRestClient(SalesforceHttpClient httpClient, String version, PayloadFormat format, SalesforceSession session) - throws SalesforceException { + public DefaultRestClient(final SalesforceHttpClient httpClient, final String version, final PayloadFormat format, + final SalesforceSession session) throws SalesforceException { super(version, session, httpClient); this.format = format; @@ -109,14 +113,11 @@ public class DefaultRestClient extends AbstractClientBase implements RestClient } return new SalesforceMultipleChoicesException(reason, statusCode, choices); } else { - final List restErrors; - if (PayloadFormat.JSON.equals(format)) { - restErrors = objectMapper.readValue(responseContent, TypeReferences.REST_ERROR_LIST_TYPE); - } else { - RestErrors errors = new RestErrors(); - xStream.fromXML(responseContent, errors); - restErrors = errors.getErrors(); + final List restErrors = readErrorsFrom(responseContent, format, objectMapper, xStream); + if (statusCode == HttpStatus.NOT_FOUND_404) { + return new NoSuchSObjectException(restErrors); } + return new SalesforceException(restErrors, statusCode); } } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/AbstractRestProcessor.java ---------------------------------------------------------------------- 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 7f4ccea..758cc57 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 @@ -34,8 +34,10 @@ import org.apache.camel.AsyncCallback; import org.apache.camel.Exchange; import org.apache.camel.Message; import org.apache.camel.TypeConverter; +import org.apache.camel.component.salesforce.NotFoundBehaviour; import org.apache.camel.component.salesforce.SalesforceEndpoint; import org.apache.camel.component.salesforce.SalesforceEndpointConfig; +import org.apache.camel.component.salesforce.api.NoSuchSObjectException; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.dto.AbstractSObjectBase; import org.apache.camel.component.salesforce.api.dto.approval.ApprovalRequest; @@ -67,10 +69,14 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor private RestClient restClient; private Map> classMap; + private final NotFoundBehaviour notFoundBehaviour; + public AbstractRestProcessor(SalesforceEndpoint endpoint) throws SalesforceException { super(endpoint); - final PayloadFormat payloadFormat = endpoint.getConfiguration().getFormat(); + final SalesforceEndpointConfig configuration = endpoint.getConfiguration(); + final PayloadFormat payloadFormat = configuration.getFormat(); + notFoundBehaviour = configuration.getNotFoundBehaviour(); this.restClient = new DefaultRestClient(httpClient, (String) endpointConfigMap.get(API_VERSION), payloadFormat, session); @@ -84,6 +90,8 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor super(endpoint); this.restClient = restClient; this.classMap = classMap; + final SalesforceEndpointConfig configuration = endpoint.getConfiguration(); + notFoundBehaviour = configuration.getNotFoundBehaviour(); } @Override @@ -831,4 +839,7 @@ public abstract class AbstractRestProcessor extends AbstractSalesforceProcessor // process response entity and set out message in exchange protected abstract void processResponse(Exchange exchange, InputStream responseEntity, SalesforceException ex, AsyncCallback callback); + final boolean shouldReport(SalesforceException ex) { + return !(ex instanceof NoSuchSObjectException && notFoundBehaviour == NotFoundBehaviour.NULL); + } } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/JsonRestProcessor.java ---------------------------------------------------------------------- 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 67a973c..d52601f 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 @@ -27,7 +27,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.AsyncCallback; import org.apache.camel.Exchange; import org.apache.camel.Message; +import org.apache.camel.component.salesforce.NotFoundBehaviour; import org.apache.camel.component.salesforce.SalesforceEndpoint; +import org.apache.camel.component.salesforce.SalesforceEndpointConfig; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.TypeReferences; import org.apache.camel.component.salesforce.api.dto.AbstractDTOBase; @@ -171,8 +173,17 @@ public class JsonRestProcessor extends AbstractRestProcessor { // process JSON response for TypeReference try { - // do we need to un-marshal a response - if (responseEntity != null) { + final Message out = exchange.getOut(); + final Message in = exchange.getIn(); + out.copyFromWithNewBody(in, null); + + if (ex != null) { + // if an exception is reported we should not loose it + if (shouldReport(ex)) { + exchange.setException(ex); + } + } else if (responseEntity != null) { + // do we need to un-marshal a response Object response = null; Class responseClass = exchange.getProperty(RESPONSE_CLASS, Class.class); if (responseClass != null) { @@ -186,13 +197,8 @@ public class JsonRestProcessor extends AbstractRestProcessor { response = responseEntity; } } - exchange.getOut().setBody(response); - } else { - exchange.setException(ex); + out.setBody(response); } - // copy headers and attachments - exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders()); - exchange.getOut().getAttachmentObjects().putAll(exchange.getIn().getAttachmentObjects()); } catch (IOException e) { String msg = "Error parsing JSON response: " + e.getMessage(); exchange.setException(new SalesforceException(msg, e)); @@ -214,5 +220,4 @@ public class JsonRestProcessor extends AbstractRestProcessor { } } - } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java index 2bbc190..6650ff0 100644 --- a/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java +++ b/components/camel-salesforce/camel-salesforce-component/src/main/java/org/apache/camel/component/salesforce/internal/processor/XmlRestProcessor.java @@ -220,12 +220,20 @@ public class XmlRestProcessor extends AbstractRestProcessor { } @Override - protected void processResponse(Exchange exchange, InputStream responseEntity, - SalesforceException exception, AsyncCallback callback) { + protected void processResponse(final Exchange exchange, final InputStream responseEntity, + final SalesforceException exception, final AsyncCallback callback) { final XStream localXStream = xStream.get(); try { - // do we need to un-marshal a response - if (responseEntity != null) { + final Message out = exchange.getOut(); + final Message in = exchange.getIn(); + out.copyFromWithNewBody(in, null); + + if (exception != null) { + if (shouldReport(exception)) { + exchange.setException(exception); + } + } else if (responseEntity != null) { + // do we need to un-marshal a response final Class responseClass = exchange.getProperty(RESPONSE_CLASS, Class.class); Object response; if (responseClass != null) { @@ -251,13 +259,8 @@ public class XmlRestProcessor extends AbstractRestProcessor { // return the response as a stream, for getBlobField response = responseEntity; } - exchange.getOut().setBody(response); - } else { - exchange.setException(exception); + out.setBody(response); } - // copy headers and attachments - exchange.getOut().getHeaders().putAll(exchange.getIn().getHeaders()); - exchange.getOut().getAttachmentObjects().putAll(exchange.getIn().getAttachmentObjects()); } catch (XStreamException e) { String msg = "Error parsing XML response: " + e.getMessage(); exchange.setException(new SalesforceException(msg, e)); http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/NotFoundIntegrationTest.java ---------------------------------------------------------------------- diff --git a/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/NotFoundIntegrationTest.java b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/NotFoundIntegrationTest.java new file mode 100644 index 0000000..8ffd8b4 --- /dev/null +++ b/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/NotFoundIntegrationTest.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.component.salesforce; + +import java.util.Arrays; + +import com.googlecode.junittoolbox.ParallelParameterized; + +import org.apache.camel.CamelExecutionException; +import org.apache.camel.component.salesforce.api.NoSuchSObjectException; +import org.apache.camel.component.salesforce.dto.generated.Account; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(ParallelParameterized.class) +public class NotFoundIntegrationTest extends AbstractSalesforceTestBase { + + @Parameter + public String format; + + @Test + public void shouldNotReportNotFoundExceptionFromRestApiIfConfiguredNotTo() { + final Account got = template + .requestBody("salesforce:getSObjectWithId?sObjectName=Account&sObjectIdName=Name&format=" + format + + "¬FoundBehaviour=NULL", "NonExistant", Account.class); + + assertNull("Expecting null when `notFoundBehaviour` is set to NULL", got); + } + + @Test + public void shouldReportNotFoundExceptionFromRestApi() { + try { + template.requestBody("salesforce:getSObjectWithId?sObjectName=Account&sObjectIdName=Name&format=" + format, + "NonExistant", Account.class); + fail("Expecting CamelExecutionException"); + } catch (final CamelExecutionException e) { + assertTrue("Expecting the cause of CamelExecutionException to be NoSuchSObjectException", + e.getCause() instanceof NoSuchSObjectException); + } + } + + @Parameters(name = "{0}") + public static Iterable formats() { + return Arrays.asList("XML", "JSON"); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/components/camel-salesforce/camel-salesforce-component/src/test/java/org/apache/camel/component/salesforce/RestApiIntegrationTest.java ---------------------------------------------------------------------- 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 716fda1..ccd09d9 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 @@ -30,6 +30,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias; import org.apache.camel.CamelExecutionException; import org.apache.camel.Processor; import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.salesforce.api.NoSuchSObjectException; import org.apache.camel.component.salesforce.api.SalesforceException; import org.apache.camel.component.salesforce.api.SalesforceMultipleChoicesException; import org.apache.camel.component.salesforce.api.dto.AbstractDTOBase; @@ -442,12 +443,11 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { template().requestBody("direct:getSObjectWithId", merchandise, Merchandise__c.class); fail("Expected SalesforceException with statusCode 300"); } catch (final CamelExecutionException e) { - assertTrue(e.getCause() instanceof SalesforceException); - assertTrue(e.getCause().getCause() instanceof SalesforceMultipleChoicesException); - final SalesforceMultipleChoicesException cause = (SalesforceMultipleChoicesException) e.getCause() - .getCause(); - assertEquals(300, cause.getStatusCode()); - final List choices = cause.getChoices(); + final Throwable cause = e.getCause(); + assertTrue(cause instanceof SalesforceMultipleChoicesException); + final SalesforceMultipleChoicesException multipleChoices = (SalesforceMultipleChoicesException) cause; + assertEquals(300, multipleChoices.getStatusCode()); + final List choices = multipleChoices.getChoices(); assertNotNull(choices); assertFalse(choices.isEmpty()); } @@ -476,12 +476,12 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { result = template().requestBody("direct:createSObject", merchandise, CreateSObjectResult.class); fail("Expected SalesforceException with statusCode 400"); } catch (final CamelExecutionException e) { - assertTrue(e.getCause() instanceof SalesforceException); - assertTrue(e.getCause().getCause() instanceof SalesforceException); - final SalesforceException cause = (SalesforceException) e.getCause().getCause(); - assertEquals(400, cause.getStatusCode()); - assertEquals(1, cause.getErrors().size()); - assertEquals("[Total_Inventory__c]", cause.getErrors().get(0).getFields().toString()); + final Throwable cause = e.getCause(); + assertTrue(cause instanceof SalesforceException); + final SalesforceException badRequest = (SalesforceException) cause; + assertEquals(400, badRequest.getStatusCode()); + assertEquals(1, badRequest.getErrors().size()); + assertEquals("[Total_Inventory__c]", badRequest.getErrors().get(0).getFields().toString()); } finally { // delete the clone if created if (result != null) { @@ -497,11 +497,11 @@ public class RestApiIntegrationTest extends AbstractSalesforceTestBase { template().requestBody("direct:getSObject", "ILLEGAL_ID", Merchandise__c.class); fail("Expected SalesforceException"); } catch (final CamelExecutionException e) { - assertTrue(e.getCause() instanceof SalesforceException); - assertTrue(e.getCause().getCause() instanceof SalesforceException); - final SalesforceException cause = (SalesforceException) e.getCause().getCause(); - assertEquals(404, cause.getStatusCode()); - assertEquals(1, cause.getErrors().size()); + final Throwable cause = e.getCause(); + assertTrue(cause instanceof NoSuchSObjectException); + final NoSuchSObjectException noSuchObject = (NoSuchSObjectException) cause; + assertEquals(404, noSuchObject.getStatusCode()); + assertEquals(1, noSuchObject.getErrors().size()); } } http://git-wip-us.apache.org/repos/asf/camel/blob/87dc6fc2/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java ---------------------------------------------------------------------- diff --git a/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java b/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java index 5ae2470..5d6ee52 100644 --- a/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java +++ b/platforms/spring-boot/components-starter/camel-salesforce-starter/src/main/java/org/apache/camel/component/salesforce/springboot/SalesforceComponentConfiguration.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.component.salesforce.AuthenticationType; +import org.apache.camel.component.salesforce.NotFoundBehaviour; import org.apache.camel.component.salesforce.SalesforceHttpClient; import org.apache.camel.component.salesforce.api.dto.analytics.reports.ReportMetadata; import org.apache.camel.component.salesforce.api.dto.approval.ApprovalRequest; @@ -622,6 +623,13 @@ public class SalesforceComponentConfiguration { * @param skipEntryCriteria */ private Boolean approvalSkipEntryCriteria; + /** + * Sets the behaviour of 404 not found status received from Salesforce + * API. Should the body be set to NULL {@link NotFoundBehaviour#NULL} or + * should a exception be signaled on the exchange + * {@link NotFoundBehaviour#EXCEPTION} - the default. + */ + private NotFoundBehaviour notFoundBehaviour; public PayloadFormat getFormat() { return format; @@ -978,6 +986,14 @@ public class SalesforceComponentConfiguration { Boolean approvalSkipEntryCriteria) { this.approvalSkipEntryCriteria = approvalSkipEntryCriteria; } + + public NotFoundBehaviour getNotFoundBehaviour() { + return notFoundBehaviour; + } + + public void setNotFoundBehaviour(NotFoundBehaviour notFoundBehaviour) { + this.notFoundBehaviour = notFoundBehaviour; + } } public static class SalesforceLoginConfigNestedConfiguration {