Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/Dapr.Client/DaprClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,50 @@ public static CallInvoker CreateInvocationInvoker(string appId, string? daprEndp
return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null)));
}

/// <summary>
/// <para>
/// Creates an <see cref="CallInvoker"/> that can be used to perform locally defined gRPC calls
/// using the Dapr sidecar as a proxy.
/// </para>
/// <para>
/// The created <see cref="CallInvoker"/> is used to intercept a <see cref="GrpcChannel"/> with an
/// <see cref="InvocationInterceptor"/>. The interceptor inserts the <paramref name="appId"/> and, if present,
/// the <paramref name="daprApiToken"/> into the request's metadata.
/// </para>
/// </summary>
/// <param name="appId">
/// The appId that is targetted by Dapr for gRPC invocations.
/// </param>
/// <param name="grpcChannelOptions">
/// The options to use when creating the <see cref="GrpcChannel"/>.
/// </param>
/// <param name="daprEndpoint">
/// Optional gRPC endpoint for calling Dapr, defaults to <see cref="DaprDefaults.GetDefaultGrpcEndpoint"/>.
/// </param>
/// <param name="daprApiToken">
/// Optional token to be attached to all requests, defaults to <see cref="DaprDefaults.GetDefaultDaprApiToken"/>.
/// </param>
/// <returns>An <see cref="CallInvoker"/> to be used for proxied gRPC calls through Dapr.</returns>
/// <remarks>
/// <para>
/// As the <paramref name="daprEndpoint"/> will remain constant, a single instance of the
/// <see cref="CallInvoker"/> can be used throughout the lifetime of the application.
/// </para>
/// </remarks>
public static CallInvoker CreateInvocationInvoker(
string appId,
GrpcChannelOptions grpcChannelOptions,
string? daprEndpoint = null,
string? daprApiToken = null)
{
ArgumentNullException.ThrowIfNull(grpcChannelOptions);

var channel = GrpcChannel.ForAddress(
daprEndpoint ?? DaprDefaults.GetDefaultGrpcEndpoint(),
grpcChannelOptions);
return channel.Intercept(new InvocationInterceptor(appId, daprApiToken ?? DaprDefaults.GetDefaultDaprApiToken(null)));
}

internal static KeyValuePair<string, string>? GetDaprApiTokenHeader(string apiToken)
{
return string.IsNullOrWhiteSpace(apiToken)
Expand Down
35 changes: 35 additions & 0 deletions test/Dapr.Client.Test/DaprClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@
// ------------------------------------------------------------------------

using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Dapr;
using Grpc.Core;
using Grpc.Net.Client;
using Xunit;

namespace Dapr.Client;
Expand Down Expand Up @@ -68,6 +73,36 @@ public void CreateInvokeHttpClient_InvalidDaprEndpoint_InvalidScheme()
Assert.Contains("The URI scheme of the Dapr endpoint must be http or https.", ex.Message);
}

[Fact]
public async Task CreateInvocationInvoker_UsesGrpcChannelOptions()
{
await using var testClient = TestClient.CreateForMessageHandler();
using var httpClient = new HttpClient(testClient.InnerClient);
var invoker = DaprClient.CreateInvocationInvoker(
appId: "bank",
grpcChannelOptions: new GrpcChannelOptions { HttpClient = httpClient },
daprEndpoint: "http://localhost");
var method = new Method<string, string>(
MethodType.Unary,
"test.Service",
"TestMethod",
Marshallers.StringMarshaller,
Marshallers.StringMarshaller);

var request = await testClient.CaptureGrpcRequestAsync(
_ => invoker.AsyncUnaryCall(method, null, new CallOptions(), "request").ResponseAsync);

Assert.Equal("bank", request.Request.Headers.GetValues("dapr-app-id").Single());
request.Dismiss();
}

[Fact]
public void CreateInvocationInvoker_WithNullGrpcChannelOptions_Throws()
{
Assert.Throws<ArgumentNullException>(
() => DaprClient.CreateInvocationInvoker(grpcChannelOptions: null!, appId: "bank"));
}

[Fact]
public void GetDaprApiTokenHeader_ApiTokenSet_SetsApiTokenHeader()
{
Expand Down
Loading