diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs index 9774daad5c..9d81e6ccd3 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/RequestOptions.cs @@ -36,6 +36,16 @@ public class RequestOptions /// public IReadOnlyDictionary Properties { get; set; } +#if PREVIEW + /// + /// Gets or sets a delegate which injects/appends a custom header in the request. + /// + public +#else + internal +#endif + Action AddRequestHeaders { get; set; } + /// /// Gets or sets the boolean to use effective partition key routing in the cosmos db request. /// @@ -79,6 +89,22 @@ internal virtual void PopulateRequestOptions(RequestMessage request) { request.Headers.Add(HttpConstants.HttpHeaders.IfNoneMatch, this.IfNoneMatchEtag); } + + this.AddRequestHeaders?.Invoke(request.Headers); + } + +#if PREVIEW + /// + /// Clone RequestOptions. + /// + /// cloned RequestOptions. + public +#else + internal +#endif + RequestOptions ShallowCopy() + { + return this.MemberwiseClone() as RequestOptions; } /// diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosHeaderTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosHeaderTests.cs index ea26efab52..6d3b5b42cb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosHeaderTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosHeaderTests.cs @@ -46,6 +46,41 @@ public async Task VerifyKnownHeaders() } } + [TestMethod] + public async Task VerifyRequestOptionCustomRequestHeaders() + { + CustomHeaderValidationHandler headerValidationHandler = new CustomHeaderValidationHandler(); + using CosmosClient client = TestCommon.CreateCosmosClient(x => x.AddCustomHandlers(headerValidationHandler)); + Database database = null; + try + { + database = await client.CreateDatabaseAsync(nameof(VerifyRequestOptionCustomRequestHeaders) + Guid.NewGuid().ToString()); + Container container = await database.CreateContainerAsync( + Guid.NewGuid().ToString(), + "/pk"); + + ToDoActivity toDoActivity = ToDoActivity.CreateRandomToDoActivity(); + ItemRequestOptions requestOptions = new ItemRequestOptions + { + AddRequestHeaders = (headers) => headers["x-ms-cosmos-database-rid"] = "databaseRidValue", + }; + + await container.CreateItemAsync(toDoActivity, new PartitionKey(toDoActivity.pk), requestOptions: requestOptions); + + // null pass + requestOptions.AddRequestHeaders = null; + + await container.ReadItemAsync(toDoActivity.id, new PartitionKey(toDoActivity.pk), requestOptions: requestOptions); + } + finally + { + if (database != null) + { + await database.DeleteStreamAsync(); + } + } + } + private class HeaderValidationHandler : RequestHandler { public override async Task SendAsync(RequestMessage request, CancellationToken cancellationToken) @@ -76,5 +111,28 @@ private void ValidateLazyHeadersAreNotCreated(CosmosMessageHeadersInternal inter } } } + + private class CustomHeaderValidationHandler : RequestHandler + { + public override async Task SendAsync(RequestMessage request, CancellationToken cancellationToken) + { + if (request.ResourceType == Documents.ResourceType.Document) + { + this.ValidateCustomHeaders(request.Headers.CosmosMessageHeaders); + } + + return await base.SendAsync(request, cancellationToken); + } + + private void ValidateCustomHeaders(CosmosMessageHeadersInternal internalHeaders) + { + string customHeaderValue = internalHeaders.Get("x-ms-cosmos-database-rid"); + + if (!string.IsNullOrEmpty(customHeaderValue)) + { + Assert.AreEqual("databaseRidValue", customHeaderValue); + } + } + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json index 47314c9ca0..eec6847024 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.json @@ -1797,7 +1797,32 @@ "NestedTypes": {} } }, - "Members": {}, + "Members": { + "Microsoft.Azure.Cosmos.RequestOptions ShallowCopy()": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.RequestOptions ShallowCopy();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Action`1[Microsoft.Azure.Cosmos.Headers] AddRequestHeaders": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "System.Action`1[Microsoft.Azure.Cosmos.Headers] AddRequestHeaders;CanRead:True;CanWrite:True;System.Action`1[Microsoft.Azure.Cosmos.Headers] get_AddRequestHeaders();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_AddRequestHeaders(System.Action`1[Microsoft.Azure.Cosmos.Headers]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Action`1[Microsoft.Azure.Cosmos.Headers] get_AddRequestHeaders()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Action`1[Microsoft.Azure.Cosmos.Headers] get_AddRequestHeaders();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_AddRequestHeaders(System.Action`1[Microsoft.Azure.Cosmos.Headers])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_AddRequestHeaders(System.Action`1[Microsoft.Azure.Cosmos.Headers]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + } + }, "NestedTypes": {} }, "Microsoft.Azure.Cosmos.TransactionalBatch;System.Object;IsAbstract:True;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": {