diff --git a/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs b/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs index 34dc3ab189..70f873d652 100644 --- a/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs +++ b/Microsoft.Azure.Cosmos/src/Authorization/TokenCredentialCache.cs @@ -221,7 +221,10 @@ private async ValueTask RefreshCachedTokenWithRetryHelperAsync(ITrace trace) throw CosmosExceptionFactory.CreateRequestTimeoutException( message: ClientResources.FailedToGetAadToken, - subStatusCode: (int)SubStatusCodes.FailedToGetAadToken, + headers: new Headers() + { + SubStatusCode = SubStatusCodes.FailedToGetAadToken, + }, innerException: lastException, trace: getTokenTrace); } @@ -253,7 +256,10 @@ private async ValueTask RefreshCachedTokenWithRetryHelperAsync(ITrace trace) throw CosmosExceptionFactory.CreateUnauthorizedException( message: ClientResources.FailedToGetAadToken, - subStatusCode: (int)SubStatusCodes.FailedToGetAadToken, + headers: new Headers() + { + SubStatusCode = SubStatusCodes.FailedToGetAadToken, + }, innerException: lastException, trace: trace); } diff --git a/Microsoft.Azure.Cosmos/src/FeedRange/FeedRanges/FeedRangePartitionKeyRange.cs b/Microsoft.Azure.Cosmos/src/FeedRange/FeedRanges/FeedRangePartitionKeyRange.cs index 6dba542bfa..18d04f0d0f 100644 --- a/Microsoft.Azure.Cosmos/src/FeedRange/FeedRanges/FeedRangePartitionKeyRange.cs +++ b/Microsoft.Azure.Cosmos/src/FeedRange/FeedRanges/FeedRangePartitionKeyRange.cs @@ -49,13 +49,12 @@ public FeedRangePartitionKeyRange(string partitionKeyRangeId) { throw CosmosExceptionFactory.Create( statusCode: HttpStatusCode.Gone, - subStatusCode: (int)SubStatusCodes.PartitionKeyRangeGone, message: $"The PartitionKeyRangeId: \"{this.PartitionKeyRangeId}\" is not valid for the current container {containerRid} .", stackTrace: string.Empty, - activityId: string.Empty, - requestCharge: 0, - retryAfter: null, - headers: null, + headers: new Headers() + { + SubStatusCode = SubStatusCodes.PartitionKeyRangeGone, + }, error: null, innerException: null, trace: NoOpTrace.Singleton); diff --git a/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs b/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs index 8ae4d5abbc..ffe9fa0395 100644 --- a/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs +++ b/Microsoft.Azure.Cosmos/src/HttpClient/CosmosHttpClientCore.cs @@ -292,6 +292,10 @@ private async Task SendHttpHelperAsync( $"GatewayStoreClient Request Timeout. Start Time UTC:{startDateTimeUtc}; Total Duration:{(DateTime.UtcNow - startDateTimeUtc).TotalMilliseconds} Ms; Request Timeout {requestTimeout.TotalMilliseconds} Ms; Http Client Timeout:{this.httpClient.Timeout.TotalMilliseconds} Ms; Activity id: {System.Diagnostics.Trace.CorrelationManager.ActivityId};"; throw CosmosExceptionFactory.CreateRequestTimeoutException( message, + headers: new Headers() + { + ActivityId = System.Diagnostics.Trace.CorrelationManager.ActivityId.ToString() + }, innerException: operationCanceledException, trace: helperTrace); } diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/ExceptionToCosmosException.cs b/Microsoft.Azure.Cosmos/src/Query/Core/ExceptionToCosmosException.cs index 1063fdac82..c2851eef1f 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/ExceptionToCosmosException.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/ExceptionToCosmosException.cs @@ -49,13 +49,12 @@ public static CosmosException CreateFromException(Exception exception) } return CosmosExceptionFactory.CreateInternalServerErrorException( - subStatusCode: default, message: exception.Message, stackTrace: exception.StackTrace, - activityId: EmptyGuidString, - requestCharge: 0, - retryAfter: null, - headers: null, + headers: new Headers() + { + ActivityId = EmptyGuidString, + }, trace: NoOpTrace.Singleton, innerException: exception); } @@ -81,13 +80,9 @@ private static CosmosException CreateFromExceptionWithStackTrace(ExceptionWithSt CosmosException cosmosException = ExceptionToCosmosException.CreateFromException(exceptionWithStackTrace.InnerException); return CosmosExceptionFactory.Create( cosmosException.StatusCode, - cosmosException.SubStatusCode, cosmosException.Message, exceptionWithStackTrace.StackTrace, - cosmosException.ActivityId, - cosmosException.RequestCharge, - cosmosException.RetryAfter, - cosmosException.Headers, + headers: cosmosException.Headers, cosmosException.Trace, cosmosException.Error, cosmosException.InnerException); @@ -103,15 +98,18 @@ private QueryExceptionConverter() public override CosmosException Visit(MalformedContinuationTokenException malformedContinuationTokenException) => CosmosExceptionFactory.CreateBadRequestException( message: malformedContinuationTokenException.Message, + headers: new Headers(), stackTrace: malformedContinuationTokenException.StackTrace, innerException: malformedContinuationTokenException); public override CosmosException Visit(UnexpectedQueryPartitionProviderException unexpectedQueryPartitionProviderException) => CosmosExceptionFactory.CreateInternalServerErrorException( message: $"{nameof(CosmosException)} due to {nameof(UnexpectedQueryPartitionProviderException)}", + headers: new Headers(), innerException: unexpectedQueryPartitionProviderException); public override CosmosException Visit(ExpectedQueryPartitionProviderException expectedQueryPartitionProviderException) => CosmosExceptionFactory.CreateBadRequestException( message: expectedQueryPartitionProviderException.Message, + headers: new Headers(), stackTrace: expectedQueryPartitionProviderException.StackTrace, innerException: expectedQueryPartitionProviderException); } @@ -127,6 +125,7 @@ private ChangeFeedExceptionConverter() internal override CosmosException Visit( MalformedChangeFeedContinuationTokenException malformedChangeFeedContinuationTokenException) => CosmosExceptionFactory.CreateBadRequestException( message: malformedChangeFeedContinuationTokenException.Message, + headers: new Headers(), stackTrace: malformedChangeFeedContinuationTokenException.StackTrace, innerException: malformedChangeFeedContinuationTokenException); } diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs index f175a39005..e74cbf75a0 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs @@ -75,7 +75,9 @@ public static async Task GetQueryPlanWithServiceI throw CosmosExceptionFactory.CreateBadRequestException( message: tryGetQueryPlan.Exception.ToString(), - stackTrace: tryGetQueryPlan.Exception.StackTrace); + headers: new Headers(), + stackTrace: tryGetQueryPlan.Exception.StackTrace, + trace: trace); } return tryGetQueryPlan.Result; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryResponseFactory.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryResponseFactory.cs index 88792ecddc..4f98084f73 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryResponseFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryResponseFactory.cs @@ -46,13 +46,13 @@ public static QueryResponseCore CreateFromException(Exception exception) else { CosmosException unkownCosmosException = CosmosExceptionFactory.CreateInternalServerErrorException( - subStatusCode: default, + headers: new Headers() + { + SubStatusCode = SubStatusCodes.PartitionKeyRangeGone, + ActivityId = QueryResponseCore.EmptyGuidString + }, message: exception.Message, stackTrace: exception.StackTrace, - activityId: QueryResponseCore.EmptyGuidString, - requestCharge: 0, - retryAfter: null, - headers: null, trace: NoOpTrace.Singleton, innerException: exception); @@ -113,17 +113,13 @@ private static QueryResponseCore CreateFromExceptionWithStackTrace(ExceptionWith statusCode: queryResponseCore.StatusCode, subStatusCodes: queryResponseCore.SubStatusCode, cosmosException: CosmosExceptionFactory.Create( - cosmosException.StatusCode, - cosmosException.SubStatusCode, - cosmosException.Message, - exceptionWithStackTrace.StackTrace, - cosmosException.ActivityId, - cosmosException.RequestCharge, - cosmosException.RetryAfter, - cosmosException.Headers, - cosmosException.Trace, - cosmosException.Error, - cosmosException.InnerException), + statusCode: cosmosException.StatusCode, + message: cosmosException.Message, + stackTrace: exceptionWithStackTrace.StackTrace, + headers: cosmosException.Headers, + trace: cosmosException.Trace, + error: cosmosException.Error, + innerException: cosmosException.InnerException), requestCharge: queryResponseCore.RequestCharge, activityId: queryResponseCore.ActivityId); @@ -142,6 +138,7 @@ public override CosmosException Visit(MalformedContinuationTokenException malfor { return CosmosExceptionFactory.CreateBadRequestException( message: malformedContinuationTokenException.Message, + headers: new Headers(), stackTrace: malformedContinuationTokenException.StackTrace, innerException: malformedContinuationTokenException); } @@ -150,6 +147,7 @@ public override CosmosException Visit(UnexpectedQueryPartitionProviderException { return CosmosExceptionFactory.CreateInternalServerErrorException( message: $"{nameof(CosmosException)} due to {nameof(UnexpectedQueryPartitionProviderException)}", + headers: new Headers(), innerException: unexpectedQueryPartitionProviderException); } @@ -157,6 +155,7 @@ public override CosmosException Visit(ExpectedQueryPartitionProviderException ex { return CosmosExceptionFactory.CreateBadRequestException( message: expectedQueryPartitionProviderException.Message, + headers: new Headers(), stackTrace: expectedQueryPartitionProviderException.StackTrace, innerException: expectedQueryPartitionProviderException); } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.cs index 106e713724..a753afa2ec 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.cs @@ -286,6 +286,7 @@ public async Task> GetFeedRangesAsync( { throw CosmosExceptionFactory.CreateInternalServerErrorException( $"Container rid {containerRId} did not have a partition key range after refresh", + headers: new Headers(), trace: trace); } @@ -299,6 +300,7 @@ public async Task> GetFeedRangesAsync( { throw CosmosExceptionFactory.CreateInternalServerErrorException( $"Container rid {containerRId} returned partitionKeyRanges null after Container RID refresh", + headers: new Headers(), trace: trace); } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs index f097c7d66d..a319f3cc7e 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs @@ -21,29 +21,20 @@ public class CosmosException : Exception internal CosmosException( HttpStatusCode statusCode, string message, - int subStatusCode, string stackTrace, - string activityId, - double requestCharge, - TimeSpan? retryAfter, Headers headers, ITrace trace, Error error, Exception innerException) : base(CosmosException.GetMessageHelper( statusCode, - subStatusCode, - message, - activityId), innerException) + headers, + message), innerException) { this.ResponseBody = message; this.stackTrace = stackTrace; - this.ActivityId = activityId; this.StatusCode = statusCode; - this.SubStatusCode = subStatusCode; - this.RetryAfter = retryAfter; - this.RequestCharge = requestCharge; - this.Headers = headers; + this.Headers = headers ?? new Headers(); this.Error = error; this.Trace = trace; } @@ -65,12 +56,19 @@ public CosmosException( : base(message) { this.stackTrace = null; - this.SubStatusCode = subStatusCode; this.StatusCode = statusCode; - this.RequestCharge = requestCharge; - this.ActivityId = activityId; - this.Headers = new Headers(); + this.ResponseBody = message; this.Trace = NoOpTrace.Singleton; + this.Headers = new Headers() + { + SubStatusCode = (SubStatusCodes)subStatusCode, + RequestCharge = requestCharge, + }; + + if (!string.IsNullOrEmpty(activityId)) + { + this.Headers.ActivityId = activityId; + } } /// @@ -88,7 +86,7 @@ public CosmosException( /// Gets the request completion sub status code from the Azure Cosmos DB service. /// /// The request completion status code - public virtual int SubStatusCode { get; } + public virtual int SubStatusCode => Headers.GetIntValueOrDefault(this.Headers.SubStatusCodeLiteral); /// /// Gets the request charge for this request from the Azure Cosmos DB service. @@ -96,7 +94,7 @@ public CosmosException( /// /// The request charge measured in request units. /// - public virtual double RequestCharge { get; } + public virtual double RequestCharge => this.Headers.RequestCharge; /// /// Gets the activity ID for the request from the Azure Cosmos DB service. @@ -104,12 +102,12 @@ public CosmosException( /// /// The activity ID for the request. /// - public virtual string ActivityId { get; } + public virtual string ActivityId => this.Headers.ActivityId; /// /// Gets the retry after time. This tells how long a request should wait before doing a retry. /// - public virtual TimeSpan? RetryAfter { get; } + public virtual TimeSpan? RetryAfter => this.Headers.RetryAfter; /// /// Gets the response headers @@ -184,28 +182,23 @@ internal ResponseMessage ToCosmosResponseMessage(RequestMessage request) cosmosException: this, statusCode: this.StatusCode, trace: this.Trace); - if (this.SubStatusCode != 0) - { - responseMessage.Headers.SubStatusCode = (SubStatusCodes)this.SubStatusCode; - } return responseMessage; } private static string GetMessageHelper( HttpStatusCode statusCode, - int subStatusCode, - string responseBody, - string activityId) + Headers headers, + string responseBody) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append($"Response status code does not indicate success: "); stringBuilder.Append($"{statusCode} ({(int)statusCode})"); stringBuilder.Append("; Substatus: "); - stringBuilder.Append(subStatusCode); + stringBuilder.Append(headers?.SubStatusCodeLiteral ?? "0" ); stringBuilder.Append("; ActivityId: "); - stringBuilder.Append(activityId ?? string.Empty); + stringBuilder.Append(headers?.ActivityId ?? string.Empty); stringBuilder.Append("; Reason: ("); stringBuilder.Append(responseBody ?? string.Empty); stringBuilder.Append(");"); diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosExceptionFactory.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosExceptionFactory.cs index c126eff2d1..2aabcfe608 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosExceptionFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosExceptionFactory.cs @@ -34,12 +34,8 @@ internal static CosmosException Create( return CosmosExceptionFactory.Create( httpStatusCode, - Headers.GetIntValueOrDefault(headers.SubStatusCodeLiteral), dce.Message, dce.StackTrace, - dce.ActivityId, - dce.RequestCharge, - dce.RetryAfter, headers, trace, dce.Error, @@ -53,12 +49,8 @@ internal static CosmosException Create( { return CosmosExceptionFactory.Create( statusCode: statusCode, - subStatusCode: default, message: errorMessage, stackTrace: null, - activityId: requestMessage?.Headers?.ActivityId, - requestCharge: 0, - retryAfter: default, headers: requestMessage?.Headers, trace: NoOpTrace.Singleton, error: default, @@ -87,12 +79,8 @@ internal static CosmosException Create( return CosmosExceptionFactory.Create( responseMessage.StatusCode, - Headers.GetIntValueOrDefault(responseMessage.Headers.SubStatusCodeLiteral), errorMessage, responseMessage?.CosmosException?.StackTrace, - responseMessage.Headers.ActivityId, - responseMessage.Headers.RequestCharge, - responseMessage.Headers.RetryAfter, responseMessage.Headers, responseMessage.Trace, error, @@ -123,12 +111,8 @@ internal static CosmosException Create( return CosmosExceptionFactory.Create( statusCode: documentServiceResponse.StatusCode, - subStatusCode: Headers.GetIntValueOrDefault(responseHeaders.SubStatusCodeLiteral), message: errorMessage, stackTrace: null, - activityId: responseHeaders.ActivityId, - requestCharge: responseHeaders.RequestCharge, - retryAfter: responseHeaders.RetryAfter, headers: responseHeaders, trace: requestMessage.Trace, error: error, @@ -154,12 +138,8 @@ internal static CosmosException Create( return CosmosExceptionFactory.Create( statusCode: storeResponse.StatusCode, - subStatusCode: Headers.GetIntValueOrDefault(headers.SubStatusCodeLiteral), message: errorMessage, stackTrace: null, - activityId: headers.ActivityId, - requestCharge: headers.RequestCharge, - retryAfter: headers.RetryAfter, headers: headers, trace: requestMessage.Trace, error: error, @@ -201,24 +181,16 @@ internal static (Error, string) GetErrorFromStream( internal static CosmosException CreateRequestTimeoutException( string message, - int subStatusCode = default, + Headers headers, string stackTrace = default, - string activityId = default, - double requestCharge = default, - TimeSpan? retryAfter = default, - Headers headers = default, ITrace trace = default, Error error = default, Exception innerException = default) { return CosmosExceptionFactory.Create( HttpStatusCode.RequestTimeout, - subStatusCode, message, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, @@ -227,24 +199,16 @@ internal static CosmosException CreateRequestTimeoutException( internal static CosmosException CreateThrottledException( string message, - int subStatusCode = default, + Headers headers, string stackTrace = default, - string activityId = default, - double requestCharge = default, - TimeSpan? retryAfter = default, - Headers headers = default, ITrace trace = default, Error error = default, Exception innerException = default) { return CosmosExceptionFactory.Create( (HttpStatusCode)StatusCodes.TooManyRequests, - subStatusCode, message, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, @@ -253,24 +217,16 @@ internal static CosmosException CreateThrottledException( internal static CosmosException CreateNotFoundException( string message, - int subStatusCode = default, + Headers headers, string stackTrace = default, - string activityId = default, - double requestCharge = default, - TimeSpan? retryAfter = default, - Headers headers = default, ITrace trace = default, Error error = default, Exception innerException = default) { return CosmosExceptionFactory.Create( HttpStatusCode.NotFound, - subStatusCode, message, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, @@ -279,24 +235,16 @@ internal static CosmosException CreateNotFoundException( internal static CosmosException CreateInternalServerErrorException( string message, - int subStatusCode = default, + Headers headers, string stackTrace = default, - string activityId = default, - double requestCharge = default, - TimeSpan? retryAfter = default, - Headers headers = default, ITrace trace = default, Error error = default, Exception innerException = default) { return CosmosExceptionFactory.Create( HttpStatusCode.InternalServerError, - subStatusCode, message, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, @@ -305,24 +253,16 @@ internal static CosmosException CreateInternalServerErrorException( internal static CosmosException CreateBadRequestException( string message, - int subStatusCode = default, + Headers headers, string stackTrace = default, - string activityId = default, - double requestCharge = default, - TimeSpan? retryAfter = default, - Headers headers = default, ITrace trace = default, Error error = default, Exception innerException = default) { return CosmosExceptionFactory.Create( HttpStatusCode.BadRequest, - subStatusCode, message, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, @@ -331,24 +271,16 @@ internal static CosmosException CreateBadRequestException( internal static CosmosException CreateUnauthorizedException( string message, - int subStatusCode, + Headers headers, Exception innerException, string stackTrace = default, - string activityId = default, - double requestCharge = default, - TimeSpan? retryAfter = default, - Headers headers = default, ITrace trace = default, Error error = default) { return CosmosExceptionFactory.Create( HttpStatusCode.Unauthorized, - subStatusCode, message, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, @@ -357,12 +289,8 @@ internal static CosmosException CreateUnauthorizedException( internal static CosmosException Create( HttpStatusCode statusCode, - int subStatusCode, string message, string stackTrace, - string activityId, - double requestCharge, - TimeSpan? retryAfter, Headers headers, ITrace trace, Error error, @@ -371,11 +299,7 @@ internal static CosmosException Create( return new CosmosException( statusCode, message, - subStatusCode, stackTrace, - activityId, - requestCharge, - retryAfter, headers, trace, error, diff --git a/Microsoft.Azure.Cosmos/src/Resource/Offer/CosmosOffers.cs b/Microsoft.Azure.Cosmos/src/Resource/Offer/CosmosOffers.cs index f9700eb955..d269a025a9 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Offer/CosmosOffers.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Offer/CosmosOffers.cs @@ -101,7 +101,10 @@ internal async Task ReplaceThroughputPropertiesIfExistsAsync { CosmosException notFound = CosmosExceptionFactory.CreateNotFoundException( $"Throughput is not configured for {targetRID}", - requestCharge: requestCharge); + headers: new Headers() + { + RequestCharge = requestCharge + }); return new ThroughputResponse( httpStatusCode: notFound.StatusCode, headers: notFound.Headers, @@ -192,7 +195,10 @@ internal Task ReplaceThroughputIfExistsAsync( { throw CosmosExceptionFactory.CreateNotFoundException( $"Throughput is not configured for {targetRID}", - requestCharge: result.requestCharge); + headers: new Headers() + { + RequestCharge = result.requestCharge + }); } return result; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs index 08e7e7f232..e03e974507 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosContainerTests.cs @@ -612,12 +612,8 @@ public async Task CreateContainerIfNotExistsAsyncTest() // Simulate a race condition which results in a 409 return CosmosExceptionFactory.Create( statusCode: HttpStatusCode.Conflict, - subStatusCode: default, message: "Fake 409 conflict", stackTrace: string.Empty, - activityId: Guid.NewGuid().ToString(), - requestCharge: response.Headers.RequestCharge, - retryAfter: default, headers: response.Headers, error: default, innerException: default, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs index 6091b84f55..ca73645a5b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosDatabaseTests.cs @@ -245,12 +245,8 @@ public async Task CreateIfNotExists() // Simulate a race condition which results in a 409 return CosmosExceptionFactory.Create( statusCode: HttpStatusCode.Conflict, - subStatusCode: default, message: "Fake 409 conflict", stackTrace: string.Empty, - activityId: Guid.NewGuid().ToString(), - requestCharge: response.Headers.RequestCharge, - retryAfter: default, headers: response.Headers, error: default, innerException: default, diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeedResultSetIteratorTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeedResultSetIteratorTests.cs index 40938ab7cb..08bafdd976 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeedResultSetIteratorTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeedResultSetIteratorTests.cs @@ -36,15 +36,16 @@ public async Task ContinuationTokenIsNotUpdatedOnFails() ResponseMessage firstResponse = new ResponseMessage(HttpStatusCode.NotModified); firstResponse.Headers.ETag = "FirstContinuation"; + Headers headers = new Headers() + { + ETag = "ShouldNotContainThis" + }; ResponseMessage secondResponse = new ResponseMessage( statusCode: HttpStatusCode.NotFound, requestMessage: null, - headers: new Headers() - { - ETag = "ShouldNotContainThis" - }, + headers: headers, trace: NoOpTrace.Singleton, - cosmosException: CosmosExceptionFactory.CreateNotFoundException("something")); + cosmosException: CosmosExceptionFactory.CreateNotFoundException("something", headers)); mockContext.SetupSequence(x => x.ProcessResourceOperationAsync( It.IsAny(), diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json index 7f8e0ed8bb..a6544b2654 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json @@ -1957,11 +1957,9 @@ "Attributes": [], "MethodInfo": "Boolean TryGetHeader(System.String, System.String ByRef);IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Double get_RequestCharge()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Double get_RequestCharge()": { "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], + "Attributes": [], "MethodInfo": "Double get_RequestCharge();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Double RequestCharge": { @@ -1969,11 +1967,9 @@ "Attributes": [], "MethodInfo": "Double RequestCharge;CanRead:True;CanWrite:False;Double get_RequestCharge();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "Int32 get_SubStatusCode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Int32 get_SubStatusCode()": { "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], + "Attributes": [], "MethodInfo": "Int32 get_SubStatusCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "Int32 SubStatusCode": { @@ -2015,11 +2011,9 @@ "Attributes": [], "MethodInfo": "System.Net.HttpStatusCode StatusCode;CanRead:True;CanWrite:False;System.Net.HttpStatusCode get_StatusCode();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.Nullable`1[System.TimeSpan] get_RetryAfter()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "System.Nullable`1[System.TimeSpan] get_RetryAfter()": { "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], + "Attributes": [], "MethodInfo": "System.Nullable`1[System.TimeSpan] get_RetryAfter();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.Nullable`1[System.TimeSpan] RetryAfter": { @@ -2032,11 +2026,9 @@ "Attributes": [], "MethodInfo": "System.String ActivityId;CanRead:True;CanWrite:False;System.String get_ActivityId();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, - "System.String get_ActivityId()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "System.String get_ActivityId()": { "Type": "Method", - "Attributes": [ - "CompilerGeneratedAttribute" - ], + "Attributes": [], "MethodInfo": "System.String get_ActivityId();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, "System.String get_ResponseBody()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs index bfb42fc3db..a880ff193f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs @@ -8,10 +8,12 @@ namespace Microsoft.Azure.Cosmos using System.Collections.Generic; using System.IO; using System.Net; + using System.Security.Cryptography; using Microsoft.Azure.Cosmos.Resource.CosmosExceptions; using Microsoft.Azure.Cosmos.Tracing; using Microsoft.Azure.Documents; using Microsoft.VisualStudio.TestTools.UnitTesting; + using Moq; using Newtonsoft.Json; [TestClass] @@ -24,6 +26,59 @@ public void EnsureSuccessStatusCode_DontThrowOnSuccess() responseMessage.EnsureSuccessStatusCode(); } + [TestMethod] + public void VerifyHeaderAlwaysExists() + { + CosmosException cosmosException = new CosmosException( + statusCode: HttpStatusCode.BadRequest, + message: "Test", + stackTrace: null, + headers: null, + trace: NoOpTrace.Singleton, + error: null, + innerException: null); + + Assert.IsNotNull(cosmosException.Headers, "Header should always be created to avoid null refs caused by users always expecting it to be there"); + } + + [TestMethod] + public void VerifyNullHeaderLogic() + { + string testMessage = "Test" + Guid.NewGuid().ToString(); + + CosmosException exception = new CosmosException( + statusCode: HttpStatusCode.BadRequest, + message: testMessage, + stackTrace: null, + headers: null, + trace: NoOpTrace.Singleton, + error: null, + innerException: null); + + Assert.IsNotNull(exception.Headers, "Header should always be created to avoid null refs caused by users always expecting it to be there"); + Assert.AreEqual(HttpStatusCode.BadRequest, exception.StatusCode); + Assert.IsTrue(exception.ToString().Contains(testMessage)); + + exception = new CosmosException( + statusCode: HttpStatusCode.BadRequest, + message: testMessage, + subStatusCode: 42, + activityId: "test", + requestCharge: 4); + + Assert.IsNotNull(exception.Headers, "Header should always be created to avoid null refs caused by users always expecting it to be there"); + Assert.AreEqual(HttpStatusCode.BadRequest, exception.StatusCode); + Assert.AreEqual(testMessage, exception.ResponseBody); + Assert.AreEqual(HttpStatusCode.BadRequest, exception.StatusCode); + Assert.AreEqual(42, exception.SubStatusCode); + Assert.AreEqual("42", exception.Headers.SubStatusCodeLiteral); + Assert.AreEqual("test", exception.ActivityId); + Assert.AreEqual("test", exception.Headers.ActivityId); + Assert.AreEqual(4, exception.RequestCharge); + Assert.AreEqual(4, exception.Headers.RequestCharge); + Assert.IsTrue(exception.ToString().Contains(testMessage)); + } + [TestMethod] public void EnsureSuccessStatusCode_ThrowsOnFailure() { @@ -163,26 +218,38 @@ public void VerifyTransportExceptionToResponseMessage() public void EnsureCorrectStatusCode() { string testMessage = "Test" + Guid.NewGuid().ToString(); - + string activityId = Guid.NewGuid().ToString(); + int substatuscode = 9000; + string substatus = substatuscode.ToString(); + double requestCharge = 42; + double retryAfter = 9000; + string retryAfterLiteral = retryAfter.ToString(); List<(HttpStatusCode statusCode, CosmosException exception)> exceptionsToStatusCodes = new List<(HttpStatusCode, CosmosException)>() { - (HttpStatusCode.NotFound, CosmosExceptionFactory.CreateNotFoundException(testMessage, activityId: Guid.NewGuid().ToString())), - (HttpStatusCode.InternalServerError, CosmosExceptionFactory.CreateInternalServerErrorException(testMessage, activityId: Guid.NewGuid().ToString())), - (HttpStatusCode.BadRequest, CosmosExceptionFactory.CreateBadRequestException(testMessage, activityId: Guid.NewGuid().ToString())), - (HttpStatusCode.RequestTimeout,CosmosExceptionFactory.CreateRequestTimeoutException(testMessage, activityId: Guid.NewGuid().ToString())), - ((HttpStatusCode)429, CosmosExceptionFactory.CreateThrottledException(testMessage, activityId: Guid.NewGuid().ToString())), + (HttpStatusCode.NotFound, CosmosExceptionFactory.CreateNotFoundException(testMessage, new Headers() { SubStatusCodeLiteral = substatus, ActivityId = activityId, RequestCharge = requestCharge, RetryAfterLiteral = retryAfterLiteral })), + (HttpStatusCode.InternalServerError, CosmosExceptionFactory.CreateInternalServerErrorException(testMessage, new Headers() {SubStatusCodeLiteral = substatus, ActivityId = activityId, RequestCharge = requestCharge, RetryAfterLiteral = retryAfterLiteral })), + (HttpStatusCode.BadRequest, CosmosExceptionFactory.CreateBadRequestException(testMessage, new Headers() {SubStatusCodeLiteral = substatus, ActivityId = activityId, RequestCharge = requestCharge, RetryAfterLiteral = retryAfterLiteral })), + (HttpStatusCode.RequestTimeout,CosmosExceptionFactory.CreateRequestTimeoutException(testMessage, new Headers() {SubStatusCodeLiteral = substatus, ActivityId = activityId, RequestCharge = requestCharge, RetryAfterLiteral = retryAfterLiteral })), + ((HttpStatusCode)429, CosmosExceptionFactory.CreateThrottledException(testMessage, new Headers() {SubStatusCodeLiteral = substatus, ActivityId = activityId, RequestCharge = requestCharge, RetryAfterLiteral = retryAfterLiteral })), }; - foreach ((HttpStatusCode statusCode, CosmosException exception) item in exceptionsToStatusCodes) + foreach ((HttpStatusCode statusCode, CosmosException exception) in exceptionsToStatusCodes) { - this.ValidateExceptionInfo(item.exception, item.statusCode, testMessage); + this.ValidateExceptionInfo( + exception, + statusCode, + substatus, + testMessage, + activityId, + requestCharge, + retryAfter); } } [TestMethod] public void ValidateExceptionStackTraceHandling() { - CosmosException cosmosException = CosmosExceptionFactory.CreateNotFoundException("TestMessage"); + CosmosException cosmosException = CosmosExceptionFactory.CreateNotFoundException("TestMessage", new Headers()); Assert.AreEqual(null, cosmosException.StackTrace); Assert.IsFalse(cosmosException.ToString().Contains(nameof(ValidateExceptionStackTraceHandling))); try @@ -197,7 +264,7 @@ public void ValidateExceptionStackTraceHandling() string stackTrace = "OriginalDocumentClientExceptionStackTrace"; try { - throw CosmosExceptionFactory.CreateNotFoundException("TestMessage", stackTrace: stackTrace); + throw CosmosExceptionFactory.CreateNotFoundException("TestMessage", new Headers(), stackTrace: stackTrace); } catch (CosmosException ce) { @@ -218,6 +285,7 @@ public void ValidateErrorHandling() CosmosException cosmosException = CosmosExceptionFactory.CreateBadRequestException( error.ToString(), + headers: new Headers(), error: error, trace: NoOpTrace.Singleton); @@ -248,14 +316,46 @@ public void ValidateErrorHandling() private void ValidateExceptionInfo( CosmosException exception, HttpStatusCode httpStatusCode, - string message) + string substatus, + string message, + string activityId, + double requestCharge, + double retryAfter) { Assert.AreEqual(message, exception.ResponseBody); Assert.AreEqual(httpStatusCode, exception.StatusCode); + Assert.AreEqual(int.Parse(substatus), exception.SubStatusCode); + Assert.AreEqual(substatus, exception.Headers.SubStatusCodeLiteral); + Assert.AreEqual(activityId, exception.ActivityId); + Assert.AreEqual(activityId, exception.Headers.ActivityId); + Assert.AreEqual(requestCharge, exception.RequestCharge); + Assert.AreEqual(requestCharge, exception.Headers.RequestCharge); + Assert.AreEqual(TimeSpan.FromMilliseconds(retryAfter), exception.RetryAfter); + Assert.AreEqual(TimeSpan.FromMilliseconds(retryAfter), exception.Headers.RetryAfter); Assert.IsTrue(exception.ToString().Contains(message)); - string expectedMessage = $"Response status code does not indicate success: {httpStatusCode} ({(int)httpStatusCode}); Substatus: 0; ActivityId: {exception.ActivityId}; Reason: ({message});"; + string expectedMessage = $"Response status code does not indicate success: {httpStatusCode} ({(int)httpStatusCode}); Substatus: {substatus}; ActivityId: {exception.ActivityId}; Reason: ({message});"; Assert.AreEqual(expectedMessage, exception.Message); + + // Verify updating the header updates the exception info + exception.Headers.SubStatusCodeLiteral = "1234"; + Assert.AreEqual(1234, exception.SubStatusCode); + Assert.AreEqual("1234", exception.Headers.SubStatusCodeLiteral); + + activityId = Guid.NewGuid().ToString(); + exception.Headers.ActivityId = activityId; + Assert.AreEqual(activityId, exception.ActivityId); + Assert.AreEqual(activityId, exception.Headers.ActivityId); + + requestCharge = 4321.09; + exception.Headers.RequestCharge = requestCharge; + Assert.AreEqual(requestCharge, exception.RequestCharge); + Assert.AreEqual(requestCharge, exception.Headers.RequestCharge); + + retryAfter = 98754; + exception.Headers.RetryAfterLiteral = retryAfter.ToString(); + Assert.AreEqual(TimeSpan.FromMilliseconds(retryAfter), exception.RetryAfter); + Assert.AreEqual(TimeSpan.FromMilliseconds(retryAfter), exception.Headers.RetryAfter); } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/FeedRange/FeedRangeResponseTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/FeedRange/FeedRangeResponseTests.cs index 1d3cb81256..d706814894 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/FeedRange/FeedRangeResponseTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/FeedRange/FeedRangeResponseTests.cs @@ -55,11 +55,12 @@ public void FeedRangeResponse_ContinuationNull_IfFailure() [TestMethod] public void FeedRangeResponse_ResponseIsAccessible() { + Headers headers = new Headers(); ResponseMessage original = new ResponseMessage( System.Net.HttpStatusCode.OK, new RequestMessage(), - new Headers(), - CosmosExceptionFactory.CreateBadRequestException("test"), + headers, + CosmosExceptionFactory.CreateBadRequestException("test", headers), NoOpTrace.Singleton) { Content = Mock.Of() diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryResponseFactoryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryResponseFactoryTests.cs index 8d3734704f..d5ad08b63a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryResponseFactoryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryResponseFactoryTests.cs @@ -21,7 +21,8 @@ public class QueryResponseFactoryTests public void CosmosException() { CosmosException cosmosException = CosmosExceptionFactory.CreateBadRequestException( - message: "asdf"); + message: "asdf", + new Headers()); QueryResponseCore queryResponse = QueryResponseFactory.CreateFromException(cosmosException); Assert.AreEqual(HttpStatusCode.BadRequest, queryResponse.StatusCode); Assert.IsNotNull(queryResponse.CosmosException); @@ -69,7 +70,7 @@ public void ExceptionFromTryCatchWithCosmosException() CosmosException cosmosException; try { - throw CosmosExceptionFactory.CreateBadRequestException("InternalServerTestMessage"); + throw CosmosExceptionFactory.CreateBadRequestException("InternalServerTestMessage", new Headers()); } catch (CosmosException ce) {