From 2450ced25fb3a6e9e9e8297287b481bdbbc07abd Mon Sep 17 00:00:00 2001 From: Whit Waldo Date: Mon, 14 Oct 2024 18:18:36 -0500 Subject: [PATCH] Adding instance-based CreateInvokableHttpClient (#1319) This PR takes the implementation of the static method and puts it into the DaprClient instance, pulling from the existing apiTokenHeader on the instance to populate the daprApiToken, pulling the endpoint from the instance's httpEndpoint value and accepting only an appId argument so as to specify the ID of the Dapr app to connect to and place in the resulting URI. --------- Signed-off-by: Whit Waldo --- src/Dapr.Client/DaprClient.cs | 26 +++++++++- src/Dapr.Client/DaprClientGrpc.cs | 25 +++++++++ ...lientTest.CreateInvokableHttpClientTest.cs | 52 +++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs diff --git a/src/Dapr.Client/DaprClient.cs b/src/Dapr.Client/DaprClient.cs index 94a43b759..9f107578f 100644 --- a/src/Dapr.Client/DaprClient.cs +++ b/src/Dapr.Client/DaprClient.cs @@ -58,7 +58,7 @@ public abstract class DaprClient : IDisposable /// The client will read the property, and /// interpret the hostname as the destination app-id. The /// property will be replaced with a new URI with the authority section replaced by - /// and the path portion of the URI rewitten to follow the format of a Dapr service invocation request. + /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request. /// /// /// @@ -448,6 +448,30 @@ public HttpRequestMessage CreateInvokeMethodRequest(string appId, stri /// A that will return the value when the operation has completed. public abstract Task InvokeMethodWithResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default); +#nullable enable + /// + /// + /// Creates an that can be used to perform Dapr service invocation using + /// objects. + /// + /// + /// The client will read the property, and + /// interpret the hostname as the destination app-id. The + /// property will be replaced with a new URI with the authority section replaced by the HTTP endpoint value + /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request. + /// + /// + /// + /// An optional app-id. If specified, the app-id will be configured as the value of + /// so that relative URIs can be used. It is mandatory to set this parameter if your app-id contains at least one upper letter. + /// If some requests use absolute URL with an app-id which contains at least one upper letter, it will not work, the workaround is to create one HttpClient for each app-id with the app-ip parameter set. + /// + /// An that can be used to perform service invocation requests. + /// + /// + public abstract HttpClient CreateInvokableHttpClient(string? appId = null); +#nullable disable + /// /// Perform service invocation using the request provided by . If the response has a non-success /// status an exception will be thrown. diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index c0c0015e0..c70aef77b 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -450,6 +450,31 @@ public override async Task InvokeMethodWithResponseAsync(Ht } } + /// + /// + /// Creates an that can be used to perform Dapr service invocation using + /// objects. + /// + /// + /// The client will read the property, and + /// interpret the hostname as the destination app-id. The + /// property will be replaced with a new URI with the authority section replaced by the instance's value + /// and the path portion of the URI rewritten to follow the format of a Dapr service invocation request. + /// + /// + /// + /// An optional app-id. If specified, the app-id will be configured as the value of + /// so that relative URIs can be used. It is mandatory to set this parameter if your app-id contains at least one upper letter. + /// If some requests use absolute URL with an app-id which contains at least one upper letter, it will not work, the workaround is to create one HttpClient for each app-id with the app-ip parameter set. + /// + /// An that can be used to perform service invocation requests. + /// + /// +#nullable enable + public override HttpClient CreateInvokableHttpClient(string? appId = null) => + DaprClient.CreateInvokeHttpClient(appId, this.httpEndpoint?.AbsoluteUri, this.apiTokenHeader?.Value); + #nullable disable + public async override Task InvokeMethodAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) { ArgumentVerifier.ThrowIfNull(request, nameof(request)); diff --git a/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs new file mode 100644 index 000000000..99fbd4972 --- /dev/null +++ b/test/Dapr.Client.Test/DaprClientTest.CreateInvokableHttpClientTest.cs @@ -0,0 +1,52 @@ +// ------------------------------------------------------------------------ +// Copyright 2024 The Dapr Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + +using System; +using Xunit; + +namespace Dapr.Client +{ + public partial class DaprClientTest + { + [Fact] + public void CreateInvokableHttpClient_WithAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + var client = daprClient.CreateInvokableHttpClient(appId: "bank"); + Assert.Equal("http://bank/", client.BaseAddress.AbsoluteUri); + } + + [Fact] + public void CreateInvokableHttpClient_InvalidAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + var ex = Assert.Throws(() => + { + // The appId needs to be something that can be used as hostname in a URI. + _ = daprClient.CreateInvokableHttpClient(appId: ""); + }); + + Assert.Contains("The appId must be a valid hostname.", ex.Message); + Assert.IsType(ex.InnerException); + } + + [Fact] + public void CreateInvokableHttpClient_WithoutAppId_FromDaprClient() + { + var daprClient = new MockClient().DaprClient; + + var client = daprClient.CreateInvokableHttpClient(); + Assert.Null(client.BaseAddress); + } + } +}