diff --git a/ShipEngine.Tests/Helpers/MockShipEngineFixture.cs b/ShipEngine.Tests/Helpers/MockShipEngineFixture.cs
index bfd91425..d5fca799 100644
--- a/ShipEngine.Tests/Helpers/MockShipEngineFixture.cs
+++ b/ShipEngine.Tests/Helpers/MockShipEngineFixture.cs
@@ -3,6 +3,7 @@ namespace ShipEngineTest
using Moq;
using Moq.Protected;
using ShipEngineSDK;
+ using System;
using System.Net;
using System.Net.Http;
using System.Text.Json;
@@ -74,10 +75,13 @@ public void AssertRequest(HttpMethod method, string path, int numberOfCalls = 1)
/// The HTTP path.
/// The status code to return.
/// The response body to return.
- public void StubRequest(HttpMethod method, string path, HttpStatusCode status, string response)
+ public string StubRequest(HttpMethod method, string path, HttpStatusCode status, string response)
{
+ var requestId = Guid.NewGuid().ToString();
var responseMessage = new HttpResponseMessage(status);
responseMessage.Content = new StringContent(response);
+ responseMessage.Headers.Add("x-shipengine-requestid", requestId);
+ responseMessage.Headers.Add("request-id", requestId);
MockHandler.Protected()
.Setup>(
@@ -87,6 +91,7 @@ public void StubRequest(HttpMethod method, string path, HttpStatusCode status, s
m.RequestUri.AbsolutePath == path),
ItExpr.IsAny())
.Returns(Task.FromResult(responseMessage));
+ return requestId;
}
}
}
\ No newline at end of file
diff --git a/ShipEngine.Tests/ShipEngineClientTests.cs b/ShipEngine.Tests/ShipEngineClientTests.cs
index dc78305d..844b0e93 100644
--- a/ShipEngine.Tests/ShipEngineClientTests.cs
+++ b/ShipEngine.Tests/ShipEngineClientTests.cs
@@ -58,7 +58,7 @@ public async Task FailureStatusWithoutShipEngineDetailsThrowsShipEngineException
var shipengine = mockShipEngineFixture.ShipEngine;
var responseBody = @"{""description"": ""valid JSON, but not what you expect""}";
- mockShipEngineFixture.StubRequest(HttpMethod.Get, "/v1/something", System.Net.HttpStatusCode.NotFound,
+ var requestId = mockShipEngineFixture.StubRequest(HttpMethod.Get, "/v1/something", System.Net.HttpStatusCode.NotFound,
responseBody);
var ex = await Assert.ThrowsAsync(
async () => await shipengine.SendHttpRequestAsync(HttpMethod.Get, "/v1/something", null,
@@ -67,6 +67,7 @@ public async Task FailureStatusWithoutShipEngineDetailsThrowsShipEngineException
Assert.NotNull(ex.ResponseMessage);
Assert.Equal(404, (int) ex.ResponseMessage.StatusCode);
+ Assert.Equal(requestId, ex.RequestId);
}
[Fact]
@@ -77,7 +78,7 @@ public async Task FailureStatusWithoutJsonContentThrowsShipEngineExceptionWithOr
var shipengine = mockShipEngineFixture.ShipEngine;
var responseBody = @"Bad Gateway
";
- mockShipEngineFixture.StubRequest(HttpMethod.Post, "/v1/something", System.Net.HttpStatusCode.BadGateway,
+ var requestId = mockShipEngineFixture.StubRequest(HttpMethod.Post, "/v1/something", System.Net.HttpStatusCode.BadGateway,
responseBody);
var ex = await Assert.ThrowsAsync(
async () => await shipengine.SendHttpRequestAsync(HttpMethod.Post, "/v1/something", "",
@@ -86,6 +87,7 @@ public async Task FailureStatusWithoutJsonContentThrowsShipEngineExceptionWithOr
Assert.NotNull(ex.ResponseMessage);
Assert.Equal(502, (int) ex.ResponseMessage.StatusCode);
+ Assert.Equal(requestId, ex.RequestId);
}
[Fact]
@@ -96,7 +98,7 @@ public async Task SuccessResponseThatCannotBeParsedThrowsExceptionWithUnparsedRe
var shipengine = mockShipEngineFixture.ShipEngine;
var responseBody = @"Unexpected response - not JSON";
- mockShipEngineFixture.StubRequest(HttpMethod.Post, "/v1/something", System.Net.HttpStatusCode.OK,
+ var requestId = mockShipEngineFixture.StubRequest(HttpMethod.Post, "/v1/something", System.Net.HttpStatusCode.OK,
responseBody);
var ex = await Assert.ThrowsAsync(
async () => await shipengine.SendHttpRequestAsync(HttpMethod.Post, "/v1/something", "",
@@ -107,6 +109,7 @@ public async Task SuccessResponseThatCannotBeParsedThrowsExceptionWithUnparsedRe
Assert.NotNull(ex.ResponseMessage);
Assert.Equal(200, (int)ex.ResponseMessage.StatusCode);
Assert.Equal(responseBody, await ex.ResponseMessage.Content.ReadAsStringAsync());
+ Assert.Equal(requestId, ex.RequestId);
}
[Fact]
@@ -118,7 +121,7 @@ public async Task SuccessResponseWithNullContentThrowsShipEngineExceptionWithUnp
// this scenario is similar to unparseable JSON - except that it is valid JSON
var responseBody = @"null";
- mockShipEngineFixture.StubRequest(HttpMethod.Post, "/v1/something", System.Net.HttpStatusCode.OK,
+ var requestId = mockShipEngineFixture.StubRequest(HttpMethod.Post, "/v1/something", System.Net.HttpStatusCode.OK,
responseBody);
var ex = await Assert.ThrowsAsync(
async () => await shipengine.SendHttpRequestAsync(HttpMethod.Post, "/v1/something", "",
@@ -130,6 +133,7 @@ public async Task SuccessResponseWithNullContentThrowsShipEngineExceptionWithUnp
Assert.Equal("Unexpected null response", ex.Message);
Assert.Equal(200, (int)ex.ResponseMessage.StatusCode);
Assert.Equal(responseBody, await ex.ResponseMessage.Content.ReadAsStringAsync());
+ Assert.Equal(requestId, ex.RequestId);
}
}
}
\ No newline at end of file
diff --git a/ShipEngine/ShipEngineClient.cs b/ShipEngine/ShipEngineClient.cs
index 99559774..9a7ee8b9 100644
--- a/ShipEngine/ShipEngineClient.cs
+++ b/ShipEngine/ShipEngineClient.cs
@@ -88,6 +88,12 @@ public static HttpClient ConfigureHttpClient(HttpClient client, string apiKey, U
private async Task DeserializedResultOrThrow(HttpResponseMessage response)
{
var contentString = await response.Content.ReadAsStringAsync();
+ string? requestId = null;
+ if (response.Headers.TryGetValues("x-shipengine-requestid", out var requestIdValues))
+ {
+ requestId = requestIdValues.FirstOrDefault();
+ }
+
if (!response.IsSuccessStatusCode)
{
@@ -104,7 +110,7 @@ private async Task DeserializedResultOrThrow(HttpResponseMessage response)
if (deserializedError == null)
{
// in this case, the response body was not parseable JSON
- throw new ShipEngineException("Unexpected HTTP status", responseMessage: response);
+ throw new ShipEngineException("Unexpected HTTP status", requestID: requestId, responseMessage: response);
}
var error = deserializedError.Errors?.FirstOrDefault(e => e.Message != null);
@@ -114,7 +120,7 @@ private async Task DeserializedResultOrThrow(HttpResponseMessage response)
error?.ErrorSource ?? ErrorSource.Shipengine,
error?.ErrorType ?? ErrorType.System,
error?.ErrorCode ?? ErrorCode.Unspecified,
- deserializedError.RequestId,
+ deserializedError.RequestId ?? requestId,
response
);
}
@@ -126,7 +132,7 @@ private async Task DeserializedResultOrThrow(HttpResponseMessage response)
}
catch (JsonException)
{
- throw new ShipEngineException("Unable to parse response", responseMessage: response);
+ throw new ShipEngineException("Unable to parse response", requestID: requestId, responseMessage: response);
}
@@ -135,7 +141,7 @@ private async Task DeserializedResultOrThrow(HttpResponseMessage response)
return result;
}
- throw new ShipEngineException(message: "Unexpected null response", responseMessage: response);
+ throw new ShipEngineException(message: "Unexpected null response", requestID: requestId, responseMessage: response);
}