Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.ServiceModel;
using System.Xml;
using static Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Notifications.Communication;

Expand Down Expand Up @@ -308,6 +309,12 @@ private static void HandleException(ConnectorBase connector, Settings settings,
{
httpStatusCode = HttpStatusCode.OK;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike the other three checks further down in this method, the new SOAP-fault branch never assigns httpStatusCode — it's left at the HttpStatusCode.OK set here. So a suppressed SOAP-fault failure comes back from e.g. GetProductsInfo as result == null with httpStatusCode == HttpStatusCode.OK, indistinguishable by status code alone from an actual success for callers that branch on httpStatusCode (e.g. the 500-based 30s request suppression in FetchProductInfosInternal-style callers).

bool skipError = false;
//Do not mark the connection as failed on SOAP faults (the service responded, but the ERP returned a business error)
if (IsSoapFault(ex))
{
skipError = true;
retry = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unconditionally disables retry for any SOAP fault, including transient business-rule faults from NAV/BC (e.g. record locking) — the PR's own motivating case.

CalculateOrder calls Communicate(..., !createOrder, ...) (see the comment above it: "only retry if is not create order"), so on the live cart-calculation path (createOrder=false) retry starts true with MaxRetryCount=3 @ 500ms. ErpServiceCaller.SendRequestInternal catches FaultException and does a bare throw;, so the fault reaches this method unwrapped and IsSoapFault matches it immediately.

Before this PR, a transient fault here got up to 3 retries; after it, one attempt and done — the request now fails outright instead of clearing on retry. Could this preserve the existing retry behavior and only suppress the endpoint-down marking (skipError), rather than also killing retry?

}
//Do not ping Endpoints with Internal Server Error 500 (that means that the request was received but generating response failed)
if (ex?.InnerException is not null)
{
Expand Down Expand Up @@ -355,6 +362,18 @@ private static void HandleException(ConnectorBase connector, Settings settings,
}
}

private static bool IsSoapFault(Exception ex)
{
for (var current = ex; current is not null; current = current.InnerException)
{
if (current is FaultException)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsSoapFault only matches System.ServiceModel.FaultException, which only ever appears when the ERP is called through ErpServiceCaller (the legacy WebServiceConnector, or an EndpointConnector using ConnectorServiceAuthenticationAddIn).

For a plain EndpointConnector (no ConnectorServiceAuthenticationAddIn — the more common Endpoint setup), EndpointHttpClient.SendRequest throws HttpRequestException on a non-2xx response, and EndpointService.HandleException re-wraps it via the 2-arg HttpRequestException(message, inner) ctor — which drops StatusCode onto the new outer exception (the original exception, which does have StatusCode, becomes .InnerException). Nothing in that chain is a FaultException, so this check never fires for it, and only the pre-existing StatusCode == InternalServerError check can suppress connector.Error(endpoint). A SOAP fault returned with any other status still reproduces the exact bug this PR targets, on that path.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separate point on the same check: it treats every FaultException the same regardless of what the fault actually represents. A SOAP fault can also indicate a genuine systemic failure on the ERP side, not just a one-off business-rule rejection — but both get classified as "skip, don't retry, don't mark down" purely based on the .NET exception type. A real ongoing outage reached this way would no longer get flagged, and EndpointMonitoringService's auto-recovery ping never kicks in.

{
return true;
}
}
return false;
}

/// <summary>
/// Retrieves a PDF document as a Base64-encoded string based on the specified request and submission type.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>10.21.9</Version>
<Version>10.21.10</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Title>Live Integration</Title>
<Description>Live Integration</Description>
Expand Down
Loading