mirror of https://github.com/dapr/dotnet-sdk.git
Include the appcallback.proto into the Dapr.Client package (#349)
* Include the appcallback.proto into the Dapr.Client package To implement service invocation for a gRPC API, a user must implement the AppCallback service on the callee site. Currently this must be done by integrating the `appcallpack.proto` file as also the depending `common.proto` file into the gRPC service application. The `Dapr.Client` package contains already the same `common.proto` file to generate the client classes. This results in a CS0433 error, because the `Dapr.Client` package and the generated AppCallback service will contain a `Dapr.Client.Autogen.Grpc.v1` namespace with the exact same classes. This pull requests integrates the `appcallpack.proto` into the client package. With this fix the user does not need to integrate the proto files by itself. See: https://gitter.im/Dapr/community?at=5f14b7e98a9a0a08cbab5d53 * Remove specific names
This commit is contained in:
parent
fcd14b278c
commit
5b246c8665
|
|
@ -4,8 +4,9 @@
|
|||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\dapr\proto\common\v1\common.proto" ProtoRoot="Protos" GrpcServices="Client" />
|
||||
<Protobuf Include="Protos\dapr\proto\common\v1\common.proto" ProtoRoot="Protos" GrpcServices="Client,Server" />
|
||||
<Protobuf Include="Protos\dapr\proto\dapr\v1\dapr.proto" ProtoRoot="Protos" GrpcServices="Client" />
|
||||
<Protobuf Include="Protos\dapr\proto\dapr\v1\appcallback.proto" ProtoRoot="Protos" GrpcServices="Server" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Additional Nuget package properties. -->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package dapr.proto.runtime.v1;
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
import "dapr/proto/common/v1/common.proto";
|
||||
|
||||
option csharp_namespace = "Dapr.AppCallback.Autogen.Grpc.v1";
|
||||
option java_outer_classname = "DaprAppCallbackProtos";
|
||||
option java_package = "io.dapr.v1";
|
||||
option go_package = "github.com/dapr/dapr/pkg/proto/runtime/v1;runtime";
|
||||
|
||||
// AppCallback V1 allows user application to interact with Dapr runtime.
|
||||
// User application needs to implement AppCallback service if it needs to
|
||||
// receive message from dapr runtime.
|
||||
service AppCallback {
|
||||
// Invokes service method with InvokeRequest.
|
||||
rpc OnInvoke (common.v1.InvokeRequest) returns (common.v1.InvokeResponse) {}
|
||||
|
||||
// Lists all topics subscribed by this app.
|
||||
rpc ListTopicSubscriptions(google.protobuf.Empty) returns (ListTopicSubscriptionsResponse) {}
|
||||
|
||||
// Subscribes events from Pubsub
|
||||
rpc OnTopicEvent(TopicEventRequest) returns (google.protobuf.Empty) {}
|
||||
|
||||
// Lists all input bindings subscribed by this app.
|
||||
rpc ListInputBindings(google.protobuf.Empty) returns (ListInputBindingsResponse) {}
|
||||
|
||||
// Listens events from the input bindings
|
||||
//
|
||||
// User application can save the states or send the events to the output
|
||||
// bindings optionally by returning BindingEventResponse.
|
||||
rpc OnBindingEvent(BindingEventRequest) returns (BindingEventResponse) {}
|
||||
}
|
||||
|
||||
// TopicEventRequest message is compatiable with CloudEvent spec v1.0
|
||||
// https://github.com/cloudevents/spec/blob/v1.0/spec.md
|
||||
message TopicEventRequest {
|
||||
// id identifies the event. Producers MUST ensure that source + id
|
||||
// is unique for each distinct event. If a duplicate event is re-sent
|
||||
// (e.g. due to a network error) it MAY have the same id.
|
||||
string id = 1;
|
||||
|
||||
// source identifies the context in which an event happened.
|
||||
// Often this will include information such as the type of the
|
||||
// event source, the organization publishing the event or the process
|
||||
// that produced the event. The exact syntax and semantics behind
|
||||
// the data encoded in the URI is defined by the event producer.
|
||||
string source = 2;
|
||||
|
||||
// The type of event related to the originating occurrence.
|
||||
string type = 3;
|
||||
|
||||
// The version of the CloudEvents specification.
|
||||
string spec_version = 4;
|
||||
|
||||
// The content type of data value.
|
||||
string data_content_type = 5;
|
||||
|
||||
// The content of the event.
|
||||
bytes data = 7;
|
||||
|
||||
// The pubsub topic which publisher sent to.
|
||||
string topic = 6;
|
||||
}
|
||||
|
||||
// BindingEventRequest represents input bindings event.
|
||||
message BindingEventRequest {
|
||||
// Requried. The name of the input binding component.
|
||||
string name = 1;
|
||||
|
||||
// Required. The payload that the input bindings sent
|
||||
bytes data = 2;
|
||||
|
||||
// The metadata set by the input binging components.
|
||||
map<string,string> metadata = 3;
|
||||
}
|
||||
|
||||
// BindingEventResponse includes operations to save state or
|
||||
// send data to output bindings optionally.
|
||||
message BindingEventResponse {
|
||||
// The name of state store where states are saved.
|
||||
string store_name = 1;
|
||||
|
||||
// The state key values which will be stored in store_name.
|
||||
repeated common.v1.StateItem states = 2;
|
||||
|
||||
// BindingEventConcurrency is the kind of concurrency
|
||||
enum BindingEventConcurrency {
|
||||
// SEQUENTIAL sends data to output bindings specified in "to" sequentially.
|
||||
SEQUENTIAL = 0;
|
||||
// PARALLEL sends data to output bindings specified in "to" in parallel.
|
||||
PARALLEL = 1;
|
||||
}
|
||||
|
||||
// The list of output bindings.
|
||||
repeated string to = 3;
|
||||
|
||||
// The content which will be sent to "to" output bindings.
|
||||
bytes data = 4;
|
||||
|
||||
// The concurrency of output bindings to send data to
|
||||
// "to" output bindings list. The default is SEQUENTIAL.
|
||||
BindingEventConcurrency concurrency = 5;
|
||||
}
|
||||
|
||||
// ListTopicSubscriptionsResponse is the message including the list of the subscribing topics.
|
||||
message ListTopicSubscriptionsResponse {
|
||||
// The list of topics.
|
||||
repeated TopicSubscription subscriptions = 1;
|
||||
}
|
||||
|
||||
// TopicSubscription represents topic and metadata.
|
||||
message TopicSubscription {
|
||||
// Required. The name of topic which will be subscribed
|
||||
string topic = 1;
|
||||
|
||||
// The optional properties used for this topic's subscribtion e.g. session id
|
||||
map<string,string> metadata = 2;
|
||||
}
|
||||
|
||||
// ListInputBindingsResponse is the message including the list of input bindings.
|
||||
message ListInputBindingsResponse {
|
||||
// The list of input bindings.
|
||||
repeated string bindings = 1;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.9.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.11.4" />
|
||||
<PackageReference Include="Grpc.Core.Testing" Version="2.31.0-pre1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.27.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Shared\AppCallbackClient.cs" />
|
||||
<Compile Include="..\Shared\TestHttpClient.cs" />
|
||||
<Compile Include="..\Shared\GrpcUtils.cs" />
|
||||
<Compile Include="..\Shared\ProtobufUtils.cs" />
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace Dapr.Client.Test
|
|||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using Dapr.AppCallback.Autogen.Grpc.v1;
|
||||
using Dapr.Client.Http;
|
||||
using FluentAssertions;
|
||||
using Grpc.Core;
|
||||
|
|
@ -37,9 +38,9 @@ namespace Dapr.Client.Test
|
|||
QueryString = queryString
|
||||
};
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokedResponse>("app1", "mymethod", httpExtension);
|
||||
var task = daprClient.InvokeMethodAsync<Response>("app1", "mymethod", httpExtension);
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
|
||||
|
|
@ -64,9 +65,9 @@ namespace Dapr.Client.Test
|
|||
.Build();
|
||||
|
||||
// httpExtension not specified
|
||||
var task = daprClient.InvokeMethodAsync<InvokedResponse>("app1", "mymethod");
|
||||
var task = daprClient.InvokeMethodAsync<Response>("app1", "mymethod");
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
|
||||
|
|
@ -87,9 +88,9 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokedResponse>("test", "test");
|
||||
var task = daprClient.InvokeMethodAsync<Response>("test", "test");
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -97,7 +98,7 @@ namespace Dapr.Client.Test
|
|||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
|
||||
// Create Response & Respond
|
||||
var data = new InvokedResponse() { Name = "Look, I was invoked!" };
|
||||
var data = new Response() { Name = "Look, I was invoked!" };
|
||||
SendResponse(data, entry);
|
||||
|
||||
// Validate Response
|
||||
|
|
@ -114,9 +115,9 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest, InvokedResponse>("test", "test", new InvokeRequest() { RequestParameter = "Hello " });
|
||||
var task = daprClient.InvokeMethodAsync<Request, Response>("test", "test", new Request() { RequestParameter = "Hello " });
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -124,11 +125,11 @@ namespace Dapr.Client.Test
|
|||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
|
||||
var json = envelope.Message.Data.Value.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json);
|
||||
var typeFromRequest = JsonSerializer.Deserialize<Request>(json);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
|
||||
// Create Response & Respond
|
||||
SendResponse<InvokedResponse>(null, entry);
|
||||
SendResponse<Response>(null, entry);
|
||||
|
||||
// Validate Response.
|
||||
var invokedResponse = await task;
|
||||
|
|
@ -145,9 +146,9 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest, InvokedResponse>("test", "test", new InvokeRequest() { RequestParameter = "Hello " });
|
||||
var task = daprClient.InvokeMethodAsync<Request, Response>("test", "test", new Request() { RequestParameter = "Hello " });
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -155,7 +156,7 @@ namespace Dapr.Client.Test
|
|||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
|
||||
var json = envelope.Message.Data.Value.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json);
|
||||
var typeFromRequest = JsonSerializer.Deserialize<Request>(json);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
|
||||
// Create Response & Respond
|
||||
|
|
@ -175,9 +176,9 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokedResponse>("test", "test");
|
||||
var task = daprClient.InvokeMethodAsync<Response>("test", "test");
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -185,7 +186,7 @@ namespace Dapr.Client.Test
|
|||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
|
||||
// Create Response & Respond
|
||||
var data = new InvokedResponse() { Name = "Look, I was invoked!" };
|
||||
var data = new Response() { Name = "Look, I was invoked!" };
|
||||
SendResponse(data, entry);
|
||||
|
||||
// Validate Response
|
||||
|
|
@ -202,9 +203,9 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokedResponse>("test", "test");
|
||||
var task = daprClient.InvokeMethodAsync<Response>("test", "test");
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -228,10 +229,10 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
InvokeRequest invokeRequest = new InvokeRequest() { RequestParameter = "Hello " };
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest>("test", "test", invokeRequest);
|
||||
Request request = new Request() { RequestParameter = "Hello " };
|
||||
var task = daprClient.InvokeMethodAsync<Request>("test", "test", request);
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -239,11 +240,11 @@ namespace Dapr.Client.Test
|
|||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
|
||||
var json = envelope.Message.Data.Value.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json);
|
||||
var typeFromRequest = JsonSerializer.Deserialize<Request>(json);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
|
||||
// Create Response & Respond
|
||||
var response = new InvokedResponse() { Name = "Look, I was invoked!" };
|
||||
var response = new Response() { Name = "Look, I was invoked!" };
|
||||
SendResponse(response, entry);
|
||||
|
||||
FluentActions.Awaiting(async () => await task).Should().NotThrow();
|
||||
|
|
@ -258,9 +259,9 @@ namespace Dapr.Client.Test
|
|||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.Build();
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest>("test", "test", new InvokeRequest() { RequestParameter = "Hello " });
|
||||
var task = daprClient.InvokeMethodAsync<Request>("test", "test", new Request() { RequestParameter = "Hello " });
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -268,7 +269,7 @@ namespace Dapr.Client.Test
|
|||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
|
||||
var json = envelope.Message.Data.Value.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json);
|
||||
var typeFromRequest = JsonSerializer.Deserialize<Request>(json);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
|
||||
// Create Response & Respond
|
||||
|
|
@ -290,10 +291,10 @@ namespace Dapr.Client.Test
|
|||
.UseJsonSerializationOptions(jsonOptions)
|
||||
.Build();
|
||||
|
||||
var invokeRequest = new InvokeRequest() { RequestParameter = "Hello" };
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest, InvokedResponse>("test", "test", invokeRequest);
|
||||
var invokeRequest = new Request() { RequestParameter = "Hello" };
|
||||
var task = daprClient.InvokeMethodAsync<Request, Response>("test", "test", invokeRequest);
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -315,12 +316,12 @@ namespace Dapr.Client.Test
|
|||
.UseJsonSerializationOptions(jsonOptions)
|
||||
.Build();
|
||||
|
||||
var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " };
|
||||
var invokedResponse = new InvokedResponse { Name = "Look, I was invoked!" };
|
||||
var invokeRequest = new Request() { RequestParameter = "Hello " };
|
||||
var invokedResponse = new Response { Name = "Look, I was invoked!" };
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest, InvokedResponse>("test", "test", invokeRequest);
|
||||
var task = daprClient.InvokeMethodAsync<Request, Response>("test", "test", invokeRequest);
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
|
|
@ -347,8 +348,8 @@ namespace Dapr.Client.Test
|
|||
.UseJsonSerializationOptions(jsonOptions)
|
||||
.Build();
|
||||
|
||||
var invokeRequest = new InvokeRequest() { RequestParameter = "Hello " };
|
||||
var invokedResponse = new InvokedResponse { Name = "Look, I was invoked!" };
|
||||
var invokeRequest = new Request() { RequestParameter = "Hello " };
|
||||
var invokedResponse = new Response { Name = "Look, I was invoked!" };
|
||||
|
||||
Dictionary<string, string> queryString = new Dictionary<string, string>();
|
||||
queryString.Add("key1", "value1");
|
||||
|
|
@ -358,13 +359,13 @@ namespace Dapr.Client.Test
|
|||
QueryString = queryString
|
||||
};
|
||||
|
||||
var task = daprClient.InvokeMethodAsync<InvokeRequest, InvokedResponse>("test", "test", invokeRequest, httpExtension);
|
||||
var task = daprClient.InvokeMethodAsync<Request, Response>("test", "test1", invokeRequest, httpExtension);
|
||||
|
||||
// Get Request and validate
|
||||
// Get Request and validate
|
||||
httpClient.Requests.TryDequeue(out var entry).Should().BeTrue();
|
||||
var envelope = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(entry.Request);
|
||||
envelope.Id.Should().Be("test");
|
||||
envelope.Message.Method.Should().Be("test");
|
||||
envelope.Message.Method.Should().Be("test1");
|
||||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationJson);
|
||||
envelope.Message.HttpExtension.Verb.Should().Be(Autogen.Grpc.v1.HTTPExtension.Types.Verb.Put);
|
||||
envelope.Message.HttpExtension.Querystring.Count.Should().Be(1);
|
||||
|
|
@ -381,6 +382,42 @@ namespace Dapr.Client.Test
|
|||
response.Name.Should().Be(invokedResponse.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeMethodAsync_AppCallback_SayHello()
|
||||
{
|
||||
// Configure Client
|
||||
var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
var httpClient = new AppCallbackClient(new DaprAppCallbackService(jsonOptions));
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.UseJsonSerializationOptions(jsonOptions)
|
||||
.Build();
|
||||
|
||||
var request = new Request() { RequestParameter = "Look, I was invoked!" };
|
||||
|
||||
var response = await daprClient.InvokeMethodAsync<Request, Response>("test1", "sayHello", request);
|
||||
|
||||
response.Name.Should().Be("Hello Look, I was invoked!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InvokeMethodAsync_AppCallback_UnexpectedMethod()
|
||||
{
|
||||
// Configure Client
|
||||
var jsonOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
var httpClient = new AppCallbackClient(new DaprAppCallbackService(jsonOptions));
|
||||
var daprClient = new DaprClientBuilder()
|
||||
.UseGrpcChannelOptions(new GrpcChannelOptions { HttpClient = httpClient })
|
||||
.UseJsonSerializationOptions(jsonOptions)
|
||||
.Build();
|
||||
|
||||
var request = new Request() { RequestParameter = "Look, I was invoked!" };
|
||||
|
||||
var response = await daprClient.InvokeMethodAsync<Request, Response>("test1", "not-existing", request);
|
||||
|
||||
response.Name.Should().Be("unexpected");
|
||||
}
|
||||
|
||||
private async void SendResponse<T>(T data, TestHttpClient.Entry entry, JsonSerializerOptions options = null)
|
||||
{
|
||||
var dataAny = ProtobufUtils.ConvertToAnyAsync(data, options);
|
||||
|
|
@ -392,14 +429,48 @@ namespace Dapr.Client.Test
|
|||
entry.Completion.SetResult(response);
|
||||
}
|
||||
|
||||
private class InvokeRequest
|
||||
private class Request
|
||||
{
|
||||
public string RequestParameter { get; set; }
|
||||
}
|
||||
|
||||
private class InvokedResponse
|
||||
private class Response
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
// Test implementation of the AppCallback.AppCallbackBase service
|
||||
private class DaprAppCallbackService : AppCallback.AppCallbackBase
|
||||
{
|
||||
private readonly JsonSerializerOptions jsonOptions;
|
||||
|
||||
public DaprAppCallbackService(JsonSerializerOptions jsonOptions)
|
||||
{
|
||||
this.jsonOptions = jsonOptions;
|
||||
}
|
||||
|
||||
public override Task<InvokeResponse> OnInvoke(InvokeRequest request, ServerCallContext context)
|
||||
{
|
||||
return request.Method switch
|
||||
{
|
||||
"sayHello" => SayHello(request),
|
||||
_ => Task.FromResult(new InvokeResponse()
|
||||
{
|
||||
Data = ProtobufUtils.ConvertToAnyAsync(new Response() { Name = $"unexpected" }, this.jsonOptions)
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
private Task<InvokeResponse> SayHello(InvokeRequest request)
|
||||
{
|
||||
var helloRequest = ProtobufUtils.ConvertFromAnyAsync<Request>(request.Data, this.jsonOptions);
|
||||
var helloResponse = new Response() { Name = $"Hello {helloRequest.RequestParameter}" };
|
||||
|
||||
return Task.FromResult(new InvokeResponse()
|
||||
{
|
||||
Data = ProtobufUtils.ConvertToAnyAsync(helloResponse, this.jsonOptions)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
namespace Dapr
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using Grpc.Core;
|
||||
using Grpc.Core.Testing;
|
||||
using Grpc.Core.Utils;
|
||||
using AppCallbackBase = AppCallback.Autogen.Grpc.v1.AppCallback.AppCallbackBase;
|
||||
|
||||
// This client will forward requests to the AppCallback service implementation which then responds to the request
|
||||
public class AppCallbackClient : HttpClient
|
||||
{
|
||||
public AppCallbackClient(AppCallbackBase callbackService)
|
||||
: this(new TestHttpClientHandler(callbackService))
|
||||
{
|
||||
}
|
||||
|
||||
private AppCallbackClient(TestHttpClientHandler handler)
|
||||
: base(handler)
|
||||
{
|
||||
}
|
||||
|
||||
private class TestHttpClientHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly AppCallbackBase callbackService;
|
||||
|
||||
public TestHttpClientHandler(AppCallbackBase callbackService)
|
||||
{
|
||||
this.callbackService = callbackService;
|
||||
}
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
|
||||
{
|
||||
var metadata = new Metadata();
|
||||
foreach (var (key, value) in httpRequest.Headers)
|
||||
{
|
||||
metadata.Add(key, string.Join(",", value.ToArray()));
|
||||
}
|
||||
|
||||
var context = TestServerCallContext.Create(
|
||||
method: httpRequest.Method.Method,
|
||||
host: httpRequest.RequestUri.Host,
|
||||
deadline: DateTime.UtcNow.AddHours(1),
|
||||
requestHeaders: metadata,
|
||||
cancellationToken: cancellationToken,
|
||||
peer: "127.0.0.1",
|
||||
authContext: null,
|
||||
contextPropagationToken: null,
|
||||
writeHeadersFunc: _ => TaskUtils.CompletedTask,
|
||||
writeOptionsGetter: () => new WriteOptions(),
|
||||
writeOptionsSetter: writeOptions => {});
|
||||
|
||||
var grpcRequest = await GrpcUtils.GetRequestFromRequestMessageAsync<InvokeServiceRequest>(httpRequest);
|
||||
var grpcResponse = await this.callbackService.OnInvoke(grpcRequest.Message, context);
|
||||
|
||||
var streamContent = await GrpcUtils.CreateResponseContent(grpcResponse);
|
||||
var httpResponse = GrpcUtils.CreateResponse(HttpStatusCode.OK, streamContent);
|
||||
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,9 +5,7 @@
|
|||
|
||||
namespace Dapr
|
||||
{
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
|
||||
|
|
@ -37,5 +35,11 @@ namespace Dapr
|
|||
|
||||
return ByteString.Empty;
|
||||
}
|
||||
|
||||
public static T ConvertFromAnyAsync<T>(Any any, JsonSerializerOptions options = null)
|
||||
{
|
||||
var utf8String = any.Value.ToStringUtf8();
|
||||
return JsonSerializer.Deserialize<T>(utf8String, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue