Skip to content

Commit

Permalink
Add yet another case of service error message
Browse files Browse the repository at this point in the history
  • Loading branch information
David R. Williamson committed Oct 19, 2022
1 parent 12fe173 commit 53d21d8
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 27 deletions.
75 changes: 48 additions & 27 deletions iothub/service/src/Exceptions/ExceptionHandlingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,8 @@ internal static async Task<Tuple<string, IotHubServiceErrorCode>> GetErrorCodeAn

try
{
IotHubExceptionResult exResult = JsonConvert.DeserializeObject<IotHubExceptionResult>(responseBody);
responseMessage = exResult.Message;

if (responseMessage != null)
{
string trackingId = string.Empty;
if (responseMessage.TrackingId != null)
{
trackingId = responseMessage.TrackingId;
}

if (responseMessage.ErrorCode != null)
{
if (int.TryParse(responseMessage.ErrorCode, NumberStyles.Any, CultureInfo.InvariantCulture, out int errorCodeInt))
{
return Tuple.Create(trackingId, (IotHubServiceErrorCode)errorCodeInt);
}
}
}
IotHubExceptionResult result = JsonConvert.DeserializeObject<IotHubExceptionResult>(responseBody);
responseMessage = result.Message;
}
catch (JsonException ex) when (ex is JsonSerializationException or JsonReaderException)
{
Expand All @@ -61,6 +44,40 @@ internal static async Task<Tuple<string, IotHubServiceErrorCode>> GetErrorCodeAn
$"Failed to parse response content JSON: {ex.Message}. Message body: '{responseBody}.'");
}

if (responseMessage == null)
{
try
{
// sometimes the message is escaped JSON :(
ResponseMessageWrapper wrapped = JsonConvert.DeserializeObject<ResponseMessageWrapper>(responseBody);
responseMessage = JsonConvert.DeserializeObject<ResponseMessage>(wrapped.Message);
}
catch (JsonException ex)
{
if (Logging.IsEnabled)
Logging.Error(
nameof(GetErrorCodeAndTrackingIdAsync),
$"Failed to parse response content JSON: {ex.Message}. Message body: '{responseBody}.'");
}
}

if (responseMessage != null)
{
string trackingId = string.Empty;
if (responseMessage.TrackingId != null)
{
trackingId = responseMessage.TrackingId;
}

if (responseMessage.ErrorCode != null)
{
if (int.TryParse(responseMessage.ErrorCode, NumberStyles.Any, CultureInfo.InvariantCulture, out int errorCodeInt))
{
return Tuple.Create(trackingId, (IotHubServiceErrorCode)errorCodeInt);
}
}
}

try
{
ResponseMessage2 rs2 = JsonConvert.DeserializeObject<ResponseMessage2>(responseBody);
Expand All @@ -77,10 +94,21 @@ internal static async Task<Tuple<string, IotHubServiceErrorCode>> GetErrorCodeAn
$"Failed to deserialize error message into ResponseMessage2: '{ex.Message}'. Message body: '{responseBody}'.");
}

IotHubExceptionResult2 exResult = null;
try
{
IotHubExceptionResult2 exResult = JsonConvert.DeserializeObject<IotHubExceptionResult2>(responseBody);
exResult = JsonConvert.DeserializeObject<IotHubExceptionResult2>(responseBody);
}
catch (JsonReaderException ex)
{
if (Logging.IsEnabled)
Logging.Error(
nameof(GetErrorCodeAndTrackingIdAsync),
$"Failed to parse response content JSON: {ex.Message}. Message body: '{responseBody}.'");
}

if (exResult != null)
{
// In some scenarios, the error response string is a semicolon delimited string with the service-returned error code
// embedded in a string response.
const char errorFieldsDelimiter = ';';
Expand Down Expand Up @@ -127,13 +155,6 @@ internal static async Task<Tuple<string, IotHubServiceErrorCode>> GetErrorCodeAn
}
}
}
catch (JsonReaderException ex)
{
if (Logging.IsEnabled)
Logging.Error(
nameof(GetErrorCodeAndTrackingIdAsync),
$"Failed to parse response content JSON: {ex.Message}. Message body: '{responseBody}.'");
}

if (Logging.IsEnabled)
Logging.Error(
Expand Down
6 changes: 6 additions & 0 deletions iothub/service/src/Exceptions/ResponseMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ internal class ResponseMessage
[JsonProperty("timestampUtc")]
internal string OccurredOnUtc { get; set; }
}

internal class ResponseMessageWrapper
{
[JsonProperty("Message")]
internal string Message { get; set; }
}
}
30 changes: 30 additions & 0 deletions iothub/service/tests/ExceptionHandlingHelperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,36 @@ public async Task GetExceptionCodeAsync_NumericErrorCode_InResponseMessage_Valid
errorCode.Should().Be(IotHubServiceErrorCode.DeviceNotOnline);
}

[TestMethod]
public async Task GetExceptionCodeAsync_MessagePayloadDoubleEscaped()
{
// arrange
const string expectedTrackingId = "95ae23a6a159445681f6a52aebc99ab0-TimeStamp:10/19/2022 16:47:22";
const IotHubServiceErrorCode expectedErrorCode = IotHubServiceErrorCode.DeviceNotOnline;

var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest);
var exceptionResult = new ResponseMessageWrapper
{
Message = JsonConvert.SerializeObject(new ResponseMessage
{
ErrorCode = ((int)expectedErrorCode).ToString(),
TrackingId = expectedTrackingId,
Message = "The operation failed because the requested device isn't online or hasn't registered the direct method callback. To learn more, see https://aka.ms/iothub404103",
OccurredOnUtc = "2022-10-19T16:47:22.0203257Z",
})
};
httpResponseMessage.Content = new StringContent(JsonConvert.SerializeObject(exceptionResult));

// act
Tuple<string, IotHubServiceErrorCode> pair = await ExceptionHandlingHelper.GetErrorCodeAndTrackingIdAsync(httpResponseMessage);
string trackingId = pair.Item1;
IotHubServiceErrorCode errorCode = pair.Item2;

// assert
trackingId.Should().Be(expectedTrackingId);
errorCode.Should().Be(expectedErrorCode);
}

[TestMethod]
public async Task GetExceptionCodeAsync_StructuredBodyFormat2()
{
Expand Down

0 comments on commit 53d21d8

Please sign in to comment.