mirror of https://github.com/dapr/dotnet-sdk.git
Use protobuf packaging for the Any type (#358)
* Use protobuf packaging for the Any type This pull request implements two converters which help with the type conversion between arbitrary types and the protobuf `Any` type. The C# protobuf library provides a mechanism to pack or unpack protobuf messages to `Any`. It provides the methods `Any.Pack` and `Any.Unpack` to serialize/deserialize messages based on `Google.Protobuf.IMessage`. For types that are not based on `Google.Protobuf.IMessage`, the existing JSON serialization/deserialization will be used. I've also cleaned the existing codebase a little bit. Fixes #268 * Fix suggested changes
This commit is contained in:
parent
ff0ead390a
commit
fdf17b7dbb
|
|
@ -11,13 +11,11 @@ namespace Dapr.Client
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dapr.Client.Autogen.Grpc.v1;
|
|
||||||
using Dapr.Client.Http;
|
using Dapr.Client.Http;
|
||||||
using Google.Protobuf;
|
|
||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
using Autogenerated = Autogen.Grpc.v1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A client for interacting with the Dapr endpoints.
|
/// A client for interacting with the Dapr endpoints.
|
||||||
|
|
@ -64,14 +62,11 @@ namespace Dapr.Client
|
||||||
|
|
||||||
if (content != null)
|
if (content != null)
|
||||||
{
|
{
|
||||||
envelope.Data = ConvertToByteStringAsync(content, this.jsonSerializerOptions);
|
envelope.Data = TypeConverters.ToJsonByteString(content, this.jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.MakeGrpcCallHandleError(
|
await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.PublishEventAsync(envelope, options),
|
||||||
{
|
|
||||||
return client.PublishEventAsync(envelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -103,17 +98,17 @@ namespace Dapr.Client
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name));
|
ArgumentVerifier.ThrowIfNullOrEmpty(name, nameof(name));
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation));
|
ArgumentVerifier.ThrowIfNullOrEmpty(operation, nameof(operation));
|
||||||
|
|
||||||
InvokeBindingResponse response = await MakeInvokeBindingRequestAsync(name, operation, data, metadata, cancellationToken);
|
Autogenerated.InvokeBindingResponse response = await MakeInvokeBindingRequestAsync(name, operation, data, metadata, cancellationToken);
|
||||||
return ConvertFromInvokeBindingResponse<TResponse>(response, this.jsonSerializerOptions);
|
return ConvertFromInvokeBindingResponse<TResponse>(response, this.jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T ConvertFromInvokeBindingResponse<T>(InvokeBindingResponse response, JsonSerializerOptions options = null)
|
private static T ConvertFromInvokeBindingResponse<T>(Autogenerated.InvokeBindingResponse response, JsonSerializerOptions options = null)
|
||||||
{
|
{
|
||||||
var responseData = response.Data.ToStringUtf8();
|
var responseData = response.Data.ToStringUtf8();
|
||||||
return JsonSerializer.Deserialize<T>(responseData, options);
|
return JsonSerializer.Deserialize<T>(responseData, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<InvokeBindingResponse> MakeInvokeBindingRequestAsync<TContent>(
|
private async Task<Autogenerated.InvokeBindingResponse> MakeInvokeBindingRequestAsync<TContent>(
|
||||||
string name,
|
string name,
|
||||||
string operation,
|
string operation,
|
||||||
TContent data,
|
TContent data,
|
||||||
|
|
@ -128,7 +123,7 @@ namespace Dapr.Client
|
||||||
|
|
||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
envelope.Data = ConvertToByteStringAsync(data, this.jsonSerializerOptions);
|
envelope.Data = TypeConverters.ToJsonByteString(data, this.jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata != null)
|
if (metadata != null)
|
||||||
|
|
@ -137,10 +132,7 @@ namespace Dapr.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.MakeGrpcCallHandleError(
|
return await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.InvokeBindingAsync(envelope, options),
|
||||||
{
|
|
||||||
return client.InvokeBindingAsync(envelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -149,7 +141,7 @@ namespace Dapr.Client
|
||||||
public override async Task InvokeMethodAsync(
|
public override async Task InvokeMethodAsync(
|
||||||
string appId,
|
string appId,
|
||||||
string methodName,
|
string methodName,
|
||||||
Http.HTTPExtension httpExtension = default,
|
HTTPExtension httpExtension = default,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
||||||
|
|
@ -162,7 +154,7 @@ namespace Dapr.Client
|
||||||
string appId,
|
string appId,
|
||||||
string methodName,
|
string methodName,
|
||||||
TRequest data,
|
TRequest data,
|
||||||
Http.HTTPExtension httpExtension = default,
|
HTTPExtension httpExtension = default,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
||||||
|
|
@ -171,7 +163,7 @@ namespace Dapr.Client
|
||||||
Any serializedData = null;
|
Any serializedData = null;
|
||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
serializedData = ConvertToAnyAsync(data, this.jsonSerializerOptions);
|
serializedData = TypeConverters.ToAny(data, this.jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = await this.MakeInvokeRequestAsync(appId, methodName, serializedData, httpExtension, cancellationToken);
|
_ = await this.MakeInvokeRequestAsync(appId, methodName, serializedData, httpExtension, cancellationToken);
|
||||||
|
|
@ -180,26 +172,21 @@ namespace Dapr.Client
|
||||||
public override async ValueTask<TResponse> InvokeMethodAsync<TResponse>(
|
public override async ValueTask<TResponse> InvokeMethodAsync<TResponse>(
|
||||||
string appId,
|
string appId,
|
||||||
string methodName,
|
string methodName,
|
||||||
Http.HTTPExtension httpExtension = default,
|
HTTPExtension httpExtension = default,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(methodName, nameof(methodName));
|
ArgumentVerifier.ThrowIfNullOrEmpty(methodName, nameof(methodName));
|
||||||
|
|
||||||
var response = await this.MakeInvokeRequestAsync(appId, methodName, null, httpExtension, cancellationToken);
|
var response = await this.MakeInvokeRequestAsync(appId, methodName, null, httpExtension, cancellationToken);
|
||||||
if (response.Data.Value.IsEmpty)
|
return response.Data.Value.IsEmpty ? default : TypeConverters.FromAny<TResponse>(response.Data, this.jsonSerializerOptions);
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConvertFromInvokeResponse<TResponse>(response, this.jsonSerializerOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<TResponse> InvokeMethodAsync<TRequest, TResponse>(
|
public override async ValueTask<TResponse> InvokeMethodAsync<TRequest, TResponse>(
|
||||||
string appId,
|
string appId,
|
||||||
string methodName,
|
string methodName,
|
||||||
TRequest data,
|
TRequest data,
|
||||||
Http.HTTPExtension httpExtension = default,
|
HTTPExtension httpExtension = default,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
ArgumentVerifier.ThrowIfNullOrEmpty(appId, nameof(appId));
|
||||||
|
|
@ -208,23 +195,18 @@ namespace Dapr.Client
|
||||||
Any serializedData = null;
|
Any serializedData = null;
|
||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
serializedData = ConvertToAnyAsync(data, this.jsonSerializerOptions);
|
serializedData = TypeConverters.ToAny(data, this.jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await this.MakeInvokeRequestAsync(appId, methodName, serializedData, httpExtension, cancellationToken);
|
var response = await this.MakeInvokeRequestAsync(appId, methodName, serializedData, httpExtension, cancellationToken);
|
||||||
if (response.Data.Value.IsEmpty)
|
return response.Data.Value.IsEmpty ? default : TypeConverters.FromAny<TResponse>(response.Data, this.jsonSerializerOptions);
|
||||||
{
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ConvertFromInvokeResponse<TResponse>(response, this.jsonSerializerOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<InvokeResponse> MakeInvokeRequestAsync(
|
private async Task<Autogenerated.InvokeResponse> MakeInvokeRequestAsync(
|
||||||
string appId,
|
string appId,
|
||||||
string methodName,
|
string methodName,
|
||||||
Any data,
|
Any data,
|
||||||
Http.HTTPExtension httpExtension,
|
HTTPExtension httpExtension,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var protoHTTPExtension = new Autogenerated.HTTPExtension();
|
var protoHTTPExtension = new Autogenerated.HTTPExtension();
|
||||||
|
|
@ -236,9 +218,9 @@ namespace Dapr.Client
|
||||||
|
|
||||||
if (httpExtension.QueryString != null)
|
if (httpExtension.QueryString != null)
|
||||||
{
|
{
|
||||||
foreach (var kv in httpExtension.QueryString)
|
foreach (var (key, value) in httpExtension.QueryString)
|
||||||
{
|
{
|
||||||
protoHTTPExtension.Querystring.Add(kv.Key, kv.Value);
|
protoHTTPExtension.Querystring.Add(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,7 +232,7 @@ namespace Dapr.Client
|
||||||
contentType = Constants.ContentTypeApplicationJson;
|
contentType = Constants.ContentTypeApplicationJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
var invokeRequest = new InvokeRequest()
|
var invokeRequest = new Autogenerated.InvokeRequest()
|
||||||
{
|
{
|
||||||
Method = methodName,
|
Method = methodName,
|
||||||
Data = data,
|
Data = data,
|
||||||
|
|
@ -266,10 +248,7 @@ namespace Dapr.Client
|
||||||
};
|
};
|
||||||
|
|
||||||
return await this.MakeGrpcCallHandleError(
|
return await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.InvokeServiceAsync(request, options),
|
||||||
{
|
|
||||||
return client.InvokeServiceAsync(request, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -281,7 +260,7 @@ namespace Dapr.Client
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
||||||
|
|
||||||
var getStateEnvelope = new GetStateRequest()
|
var getStateEnvelope = new Autogenerated.GetStateRequest()
|
||||||
{
|
{
|
||||||
StoreName = storeName,
|
StoreName = storeName,
|
||||||
Key = key,
|
Key = key,
|
||||||
|
|
@ -293,10 +272,7 @@ namespace Dapr.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await this.MakeGrpcCallHandleError(
|
var response = await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.GetStateAsync(getStateEnvelope, options),
|
||||||
{
|
|
||||||
return client.GetStateAsync(getStateEnvelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
if (response.Data.IsEmpty)
|
if (response.Data.IsEmpty)
|
||||||
|
|
@ -314,7 +290,7 @@ namespace Dapr.Client
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
||||||
|
|
||||||
var getStateEnvelope = new GetStateRequest()
|
var getStateEnvelope = new Autogenerated.GetStateRequest()
|
||||||
{
|
{
|
||||||
StoreName = storeName,
|
StoreName = storeName,
|
||||||
Key = key,
|
Key = key,
|
||||||
|
|
@ -326,15 +302,12 @@ namespace Dapr.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await this.MakeGrpcCallHandleError(
|
var response = await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.GetStateAsync(getStateEnvelope, options),
|
||||||
{
|
|
||||||
return client.GetStateAsync(getStateEnvelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
if (response.Data.IsEmpty)
|
if (response.Data.IsEmpty)
|
||||||
{
|
{
|
||||||
return (default(TValue), response.Etag);
|
return (default, response.Etag);
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseData = response.Data.ToStringUtf8();
|
var responseData = response.Data.ToStringUtf8();
|
||||||
|
|
@ -354,7 +327,7 @@ namespace Dapr.Client
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
||||||
|
|
||||||
await this.MakeSaveStateCallAsync<TValue>(
|
await this.MakeSaveStateCallAsync(
|
||||||
storeName,
|
storeName,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
|
|
@ -379,16 +352,17 @@ namespace Dapr.Client
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await this.MakeSaveStateCallAsync<TValue>(storeName, key, value, etag, stateOptions, metadata, cancellationToken);
|
await this.MakeSaveStateCallAsync(storeName, key, value, etag, stateOptions, metadata, cancellationToken);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (RpcException)
|
catch (RpcException)
|
||||||
{ }
|
{
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async ValueTask MakeSaveStateCallAsync<TValue>(
|
private async ValueTask MakeSaveStateCallAsync<TValue>(
|
||||||
string storeName,
|
string storeName,
|
||||||
string key,
|
string key,
|
||||||
TValue value,
|
TValue value,
|
||||||
|
|
@ -425,16 +399,13 @@ namespace Dapr.Client
|
||||||
|
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
stateItem.Value = ConvertToByteStringAsync(value, this.jsonSerializerOptions);
|
stateItem.Value = TypeConverters.ToJsonByteString(value, this.jsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveStateEnvelope.States.Add(stateItem);
|
saveStateEnvelope.States.Add(stateItem);
|
||||||
|
|
||||||
await this.MakeGrpcCallHandleError(
|
await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.SaveStateAsync(saveStateEnvelope, options),
|
||||||
{
|
|
||||||
return client.SaveStateAsync(saveStateEnvelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -448,7 +419,7 @@ namespace Dapr.Client
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
ArgumentVerifier.ThrowIfNullOrEmpty(storeName, nameof(storeName));
|
||||||
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
ArgumentVerifier.ThrowIfNullOrEmpty(key, nameof(key));
|
||||||
|
|
||||||
await this.MakeDeleteStateCallsync(
|
await this.MakeDeleteStateCallAsync(
|
||||||
storeName,
|
storeName,
|
||||||
key,
|
key,
|
||||||
etag: null,
|
etag: null,
|
||||||
|
|
@ -460,7 +431,7 @@ namespace Dapr.Client
|
||||||
public override async ValueTask<bool> TryDeleteStateAsync(
|
public override async ValueTask<bool> TryDeleteStateAsync(
|
||||||
string storeName,
|
string storeName,
|
||||||
string key,
|
string key,
|
||||||
string etag = default,
|
string etag,
|
||||||
StateOptions stateOptions = default,
|
StateOptions stateOptions = default,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
|
@ -469,7 +440,7 @@ namespace Dapr.Client
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await this.MakeDeleteStateCallsync(storeName, key, etag, stateOptions, cancellationToken);
|
await this.MakeDeleteStateCallAsync(storeName, key, etag, stateOptions, cancellationToken);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
|
|
@ -479,14 +450,14 @@ namespace Dapr.Client
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ValueTask MakeDeleteStateCallsync(
|
private async ValueTask MakeDeleteStateCallAsync(
|
||||||
string storeName,
|
string storeName,
|
||||||
string key,
|
string key,
|
||||||
string etag = default,
|
string etag = default,
|
||||||
StateOptions stateOptions = default,
|
StateOptions stateOptions = default,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var deleteStateEnvelope = new DeleteStateRequest()
|
var deleteStateEnvelope = new Autogenerated.DeleteStateRequest()
|
||||||
{
|
{
|
||||||
StoreName = storeName,
|
StoreName = storeName,
|
||||||
Key = key,
|
Key = key,
|
||||||
|
|
@ -503,10 +474,7 @@ namespace Dapr.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.MakeGrpcCallHandleError(
|
await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.DeleteStateAsync(deleteStateEnvelope, options),
|
||||||
{
|
|
||||||
return client.DeleteStateAsync(deleteStateEnvelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -534,10 +502,7 @@ namespace Dapr.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = await this.MakeGrpcCallHandleError(
|
var response = await this.MakeGrpcCallHandleError(
|
||||||
(options) =>
|
options => client.GetSecretAsync(envelope, options),
|
||||||
{
|
|
||||||
return client.GetSecretAsync(envelope, options);
|
|
||||||
},
|
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
return response.Data.ToDictionary(kv => kv.Key, kv => kv.Value);
|
return response.Data.ToDictionary(kv => kv.Key, kv => kv.Value);
|
||||||
|
|
@ -576,60 +541,32 @@ namespace Dapr.Client
|
||||||
stateRequestOptions.Concurrency = GetStateConcurrencyForConcurrencyMode(stateOptions.Concurrency.Value);
|
stateRequestOptions.Concurrency = GetStateConcurrencyForConcurrencyMode(stateOptions.Concurrency.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stateOptions.RetryOptions != null)
|
if (stateOptions.RetryOptions == null)
|
||||||
{
|
{
|
||||||
var retryPolicy = new Autogenerated.StateRetryPolicy();
|
return stateRequestOptions;
|
||||||
if (stateOptions.RetryOptions.RetryMode != null)
|
|
||||||
{
|
|
||||||
retryPolicy.Pattern = GetRetryPatternForRetryMode(stateOptions.RetryOptions.RetryMode.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stateOptions.RetryOptions.RetryInterval != null)
|
|
||||||
{
|
|
||||||
retryPolicy.Interval = Duration.FromTimeSpan(stateOptions.RetryOptions.RetryInterval.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stateOptions.RetryOptions.RetryThreshold != null)
|
|
||||||
{
|
|
||||||
retryPolicy.Threshold = stateOptions.RetryOptions.RetryThreshold.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
stateRequestOptions.RetryPolicy = retryPolicy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var retryPolicy = new Autogenerated.StateRetryPolicy();
|
||||||
|
if (stateOptions.RetryOptions.RetryMode != null)
|
||||||
|
{
|
||||||
|
retryPolicy.Pattern = GetRetryPatternForRetryMode(stateOptions.RetryOptions.RetryMode.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateOptions.RetryOptions.RetryInterval != null)
|
||||||
|
{
|
||||||
|
retryPolicy.Interval = Duration.FromTimeSpan(stateOptions.RetryOptions.RetryInterval.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stateOptions.RetryOptions.RetryThreshold != null)
|
||||||
|
{
|
||||||
|
retryPolicy.Threshold = stateOptions.RetryOptions.RetryThreshold.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateRequestOptions.RetryPolicy = retryPolicy;
|
||||||
|
|
||||||
return stateRequestOptions;
|
return stateRequestOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Any ConvertToAnyAsync<T>(T data, JsonSerializerOptions options = null)
|
|
||||||
{
|
|
||||||
var any = new Any();
|
|
||||||
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options);
|
|
||||||
any.Value = ByteString.CopyFrom(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return any;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ByteString ConvertToByteStringAsync<T>(T data, JsonSerializerOptions options = null)
|
|
||||||
{
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options);
|
|
||||||
return ByteString.CopyFrom(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ByteString.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T ConvertFromInvokeResponse<T>(InvokeResponse response, JsonSerializerOptions options = null)
|
|
||||||
{
|
|
||||||
var responseData = response.Data.Value.ToStringUtf8();
|
|
||||||
return JsonSerializer.Deserialize<T>(responseData, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Autogenerated.HTTPExtension.Types.Verb ConvertHTTPVerb(HTTPVerb verb)
|
private static Autogenerated.HTTPExtension.Types.Verb ConvertHTTPVerb(HTTPVerb verb)
|
||||||
{
|
{
|
||||||
return verb switch
|
return verb switch
|
||||||
|
|
@ -648,49 +585,33 @@ namespace Dapr.Client
|
||||||
|
|
||||||
private static Autogenerated.StateOptions.Types.StateConsistency GetStateConsistencyForConsistencyMode(ConsistencyMode consistencyMode)
|
private static Autogenerated.StateOptions.Types.StateConsistency GetStateConsistencyForConsistencyMode(ConsistencyMode consistencyMode)
|
||||||
{
|
{
|
||||||
if (consistencyMode.Equals(ConsistencyMode.Eventual))
|
return consistencyMode switch
|
||||||
{
|
{
|
||||||
return Autogenerated.StateOptions.Types.StateConsistency.ConsistencyEventual;
|
ConsistencyMode.Eventual => Autogenerated.StateOptions.Types.StateConsistency.ConsistencyEventual,
|
||||||
}
|
ConsistencyMode.Strong => Autogenerated.StateOptions.Types.StateConsistency.ConsistencyStrong,
|
||||||
|
_ => throw new ArgumentException($"{consistencyMode} Consistency Mode is not supported.")
|
||||||
if (consistencyMode.Equals(ConsistencyMode.Strong))
|
};
|
||||||
{
|
|
||||||
return Autogenerated.StateOptions.Types.StateConsistency.ConsistencyStrong;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"{consistencyMode} Consistency Mode is not supported.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Autogenerated.StateOptions.Types.StateConcurrency GetStateConcurrencyForConcurrencyMode(ConcurrencyMode concurrencyMode)
|
private static Autogenerated.StateOptions.Types.StateConcurrency GetStateConcurrencyForConcurrencyMode(ConcurrencyMode concurrencyMode)
|
||||||
{
|
{
|
||||||
if (concurrencyMode.Equals(ConcurrencyMode.FirstWrite))
|
return concurrencyMode switch
|
||||||
{
|
{
|
||||||
return Autogenerated.StateOptions.Types.StateConcurrency.ConcurrencyFirstWrite;
|
ConcurrencyMode.FirstWrite => Autogenerated.StateOptions.Types.StateConcurrency.ConcurrencyFirstWrite,
|
||||||
}
|
ConcurrencyMode.LastWrite => Autogenerated.StateOptions.Types.StateConcurrency.ConcurrencyLastWrite,
|
||||||
|
_ => throw new ArgumentException($"{concurrencyMode} Concurrency Mode is not supported.")
|
||||||
if (concurrencyMode.Equals(ConcurrencyMode.LastWrite))
|
};
|
||||||
{
|
|
||||||
return Autogenerated.StateOptions.Types.StateConcurrency.ConcurrencyLastWrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"{concurrencyMode} Concurrency Mode is not supported.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Autogenerated.StateRetryPolicy.Types.RetryPattern GetRetryPatternForRetryMode(RetryMode retryMode)
|
private static Autogenerated.StateRetryPolicy.Types.RetryPattern GetRetryPatternForRetryMode(RetryMode retryMode)
|
||||||
{
|
{
|
||||||
if (retryMode.Equals(RetryMode.Exponential))
|
return retryMode switch
|
||||||
{
|
{
|
||||||
return Autogenerated.StateRetryPolicy.Types.RetryPattern.RetryExponential;
|
RetryMode.Exponential => Autogenerated.StateRetryPolicy.Types.RetryPattern.RetryExponential,
|
||||||
}
|
RetryMode.Linear => Autogenerated.StateRetryPolicy.Types.RetryPattern.RetryLinear,
|
||||||
|
_ => throw new ArgumentException($"{retryMode} Retry Mode is not supported.")
|
||||||
if (retryMode.Equals(RetryMode.Linear))
|
};
|
||||||
{
|
|
||||||
return Autogenerated.StateRetryPolicy.Types.RetryPattern.RetryLinear;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"{retryMode} Retry Mode is not supported.");
|
|
||||||
}
|
}
|
||||||
#endregion Helper Methods
|
#endregion Helper Methods
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Dapr.Client
|
||||||
|
{
|
||||||
|
using System.Text.Json;
|
||||||
|
using Google.Protobuf;
|
||||||
|
using Google.Protobuf.WellKnownTypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Some type converters.
|
||||||
|
/// </summary>
|
||||||
|
internal static class TypeConverters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an arbitrary type to a <see cref="System.Text.Json"/> based <see cref="ByteString"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The data to convert.</param>
|
||||||
|
/// <param name="options">The JSON serialization options.</param>
|
||||||
|
/// <typeparam name="T">The type of the given data.</typeparam>
|
||||||
|
/// <returns>The given data as JSON based byte string.</returns>
|
||||||
|
public static ByteString ToJsonByteString<T>(T data, JsonSerializerOptions options = null)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return ByteString.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options);
|
||||||
|
return ByteString.CopyFrom(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts an arbitrary type to the <see cref="Any"/> Protocol Buffer type.
|
||||||
|
///
|
||||||
|
/// If the given type is a subtype of <see cref="IMessage"/>, then it's save to use the Protocol Buffer
|
||||||
|
/// packaging provided for the <see cref="Any"/> type with the <see cref="Any.Pack(Google.Protobuf.IMessage)"/>.
|
||||||
|
/// For all other types, we use JSON serialization based on <see cref="System.Text.Json"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">The data to convert.</param>
|
||||||
|
/// <param name="options">The JSON serialization options.</param>
|
||||||
|
/// <typeparam name="T">The type of the given data.</typeparam>
|
||||||
|
/// <returns>The <see cref="Any"/> type representation of the given data.</returns>
|
||||||
|
public static Any ToAny<T>(T data, JsonSerializerOptions options = null)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return new Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
var t = typeof(T);
|
||||||
|
|
||||||
|
return typeof(IMessage).IsAssignableFrom(t)
|
||||||
|
? Any.Pack((IMessage) data)
|
||||||
|
: new Any {Value = ToJsonByteString(data, options), TypeUrl = t.FullName};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the Protocol Buffer <see cref="Any"/> type to an arbitrary type.
|
||||||
|
///
|
||||||
|
/// If the type to convert to is a subtype of <see cref="IMessage"/> and if the type has an empty default
|
||||||
|
/// constructor, then we use the <see cref="Any.Unpack{T}"/> method to deserialize the given <see cref="Any"/>
|
||||||
|
/// instance. For all other types, we use JSON deserialization based on <see cref="System.Text.Json"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="any">The any instance to convert.</param>
|
||||||
|
/// <param name="options">The JSON serialization options.</param>
|
||||||
|
/// <typeparam name="T">The type to convert to.</typeparam>
|
||||||
|
/// <returns>An instance of T.</returns>
|
||||||
|
public static T FromAny<T>(Any any, JsonSerializerOptions options = null)
|
||||||
|
{
|
||||||
|
var t = typeof(T);
|
||||||
|
|
||||||
|
if (typeof(IMessage).IsAssignableFrom(t) && t.GetConstructor(System.Type.EmptyTypes) != null)
|
||||||
|
{
|
||||||
|
var method = any.GetType().GetMethod("Unpack");
|
||||||
|
var generic = method.MakeGenericMethod(t);
|
||||||
|
return (T) generic.Invoke(any, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T>(any.Value.ToStringUtf8(), options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\Shared\TestHttpClient.cs" />
|
<Compile Include="..\Shared\TestHttpClient.cs" />
|
||||||
<Compile Include="..\Shared\GrpcUtils.cs" />
|
<Compile Include="..\Shared\GrpcUtils.cs" />
|
||||||
<Compile Include="..\Shared\ProtobufUtils.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Dapr.AspNetCore.Test
|
||||||
|
|
||||||
private async void SendResponseWithState<T>(T state, TestHttpClient.Entry entry)
|
private async void SendResponseWithState<T>(T state, TestHttpClient.Entry entry)
|
||||||
{
|
{
|
||||||
var stateData = ProtobufUtils.ConvertToByteStringAsync(state);
|
var stateData = TypeConverters.ToJsonByteString(state);
|
||||||
var stateResponse = new GetStateResponse();
|
var stateResponse = new GetStateResponse();
|
||||||
stateResponse.Data = stateData;
|
stateResponse.Data = stateData;
|
||||||
stateResponse.Etag = "test";
|
stateResponse.Etag = "test";
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.11.4" />
|
<PackageReference Include="Google.Protobuf" Version="3.11.4" />
|
||||||
<PackageReference Include="Grpc.Core.Testing" Version="2.31.0-pre1" />
|
<PackageReference Include="Grpc.Core.Testing" Version="2.31.0-pre1" />
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.27.0" />
|
<PackageReference Include="Grpc.Net.Client" Version="2.27.0" />
|
||||||
|
<PackageReference Include="Grpc.Tools" Version="2.27.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||||
|
|
@ -16,11 +17,14 @@
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Protobuf Include="Protos\test.proto" ProtoRoot="Protos" GrpcServices="Client" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\Shared\AppCallbackClient.cs" />
|
<Compile Include="..\Shared\AppCallbackClient.cs" />
|
||||||
<Compile Include="..\Shared\TestHttpClient.cs" />
|
<Compile Include="..\Shared\TestHttpClient.cs" />
|
||||||
<Compile Include="..\Shared\GrpcUtils.cs" />
|
<Compile Include="..\Shared\GrpcUtils.cs" />
|
||||||
<Compile Include="..\Shared\ProtobufUtils.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace Dapr.Client.Test
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dapr.Client.Autogen.Grpc.v1;
|
using Dapr.Client.Autogen.Grpc.v1;
|
||||||
|
using Dapr.Client.Autogen.Test.Grpc.v1;
|
||||||
using Dapr.AppCallback.Autogen.Grpc.v1;
|
using Dapr.AppCallback.Autogen.Grpc.v1;
|
||||||
using Dapr.Client.Http;
|
using Dapr.Client.Http;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
|
@ -395,11 +396,35 @@ namespace Dapr.Client.Test
|
||||||
|
|
||||||
var request = new Request() { RequestParameter = "Look, I was invoked!" };
|
var request = new Request() { RequestParameter = "Look, I was invoked!" };
|
||||||
|
|
||||||
var response = await daprClient.InvokeMethodAsync<Request, Response>("test1", "sayHello", request);
|
var response = await daprClient.InvokeMethodAsync<Request, Response>("test", "SayHello", request);
|
||||||
|
|
||||||
response.Name.Should().Be("Hello Look, I was invoked!");
|
response.Name.Should().Be("Hello Look, I was invoked!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InvokeMethodAsync_AppCallback_RepeatedField()
|
||||||
|
{
|
||||||
|
// 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 testRun = new TestRun();
|
||||||
|
testRun.Tests.Add(new TestCase() { Name = "test1" });
|
||||||
|
testRun.Tests.Add(new TestCase() { Name = "test2" });
|
||||||
|
testRun.Tests.Add(new TestCase() { Name = "test3" });
|
||||||
|
|
||||||
|
var response = await daprClient.InvokeMethodAsync<TestRun, TestRun>("test", "TestRun", testRun);
|
||||||
|
|
||||||
|
response.Tests.Count.Should().Be(3);
|
||||||
|
response.Tests[0].Name.Should().Be("test1");
|
||||||
|
response.Tests[1].Name.Should().Be("test2");
|
||||||
|
response.Tests[2].Name.Should().Be("test3");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task InvokeMethodAsync_AppCallback_UnexpectedMethod()
|
public async Task InvokeMethodAsync_AppCallback_UnexpectedMethod()
|
||||||
{
|
{
|
||||||
|
|
@ -413,14 +438,14 @@ namespace Dapr.Client.Test
|
||||||
|
|
||||||
var request = new Request() { RequestParameter = "Look, I was invoked!" };
|
var request = new Request() { RequestParameter = "Look, I was invoked!" };
|
||||||
|
|
||||||
var response = await daprClient.InvokeMethodAsync<Request, Response>("test1", "not-existing", request);
|
var response = await daprClient.InvokeMethodAsync<Request, Response>("test", "not-existing", request);
|
||||||
|
|
||||||
response.Name.Should().Be("unexpected");
|
response.Name.Should().Be("unexpected");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SendResponse<T>(T data, TestHttpClient.Entry entry, JsonSerializerOptions options = null)
|
private async void SendResponse<T>(T data, TestHttpClient.Entry entry, JsonSerializerOptions options = null)
|
||||||
{
|
{
|
||||||
var dataAny = ProtobufUtils.ConvertToAnyAsync(data, options);
|
var dataAny = TypeConverters.ToAny(data, options);
|
||||||
var dataResponse = new InvokeResponse();
|
var dataResponse = new InvokeResponse();
|
||||||
dataResponse.Data = dataAny;
|
dataResponse.Data = dataAny;
|
||||||
|
|
||||||
|
|
@ -453,22 +478,33 @@ namespace Dapr.Client.Test
|
||||||
{
|
{
|
||||||
return request.Method switch
|
return request.Method switch
|
||||||
{
|
{
|
||||||
"sayHello" => SayHello(request),
|
"SayHello" => SayHello(request),
|
||||||
|
"TestRun" => TestRun(request),
|
||||||
_ => Task.FromResult(new InvokeResponse()
|
_ => Task.FromResult(new InvokeResponse()
|
||||||
{
|
{
|
||||||
Data = ProtobufUtils.ConvertToAnyAsync(new Response() { Name = $"unexpected" }, this.jsonOptions)
|
Data = TypeConverters.ToAny(new Response() { Name = $"unexpected" }, this.jsonOptions)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<InvokeResponse> SayHello(InvokeRequest request)
|
private Task<InvokeResponse> SayHello(InvokeRequest request)
|
||||||
{
|
{
|
||||||
var helloRequest = ProtobufUtils.ConvertFromAnyAsync<Request>(request.Data, this.jsonOptions);
|
var helloRequest = TypeConverters.FromAny<Request>(request.Data, this.jsonOptions);
|
||||||
var helloResponse = new Response() { Name = $"Hello {helloRequest.RequestParameter}" };
|
var helloResponse = new Response() { Name = $"Hello {helloRequest.RequestParameter}" };
|
||||||
|
|
||||||
return Task.FromResult(new InvokeResponse()
|
return Task.FromResult(new InvokeResponse()
|
||||||
{
|
{
|
||||||
Data = ProtobufUtils.ConvertToAnyAsync(helloResponse, this.jsonOptions)
|
Data = TypeConverters.ToAny(helloResponse, this.jsonOptions)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<InvokeResponse> TestRun(InvokeRequest request)
|
||||||
|
{
|
||||||
|
var echoRequest = TypeConverters.FromAny<TestRun>(request.Data, this.jsonOptions);
|
||||||
|
|
||||||
|
return Task.FromResult(new InvokeResponse()
|
||||||
|
{
|
||||||
|
Data = TypeConverters.ToAny(echoRequest, this.jsonOptions)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option csharp_namespace = "Dapr.Client.Autogen.Test.Grpc.v1";
|
||||||
|
|
||||||
|
message TestRun {
|
||||||
|
repeated TestCase tests = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TestCase {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,6 @@ namespace Dapr.Client.Test
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
using Grpc.Net.Client;
|
using Grpc.Net.Client;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Google.Protobuf.WellKnownTypes;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency;
|
using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency;
|
||||||
|
|
@ -584,7 +583,7 @@ namespace Dapr.Client.Test
|
||||||
|
|
||||||
private async void SendResponseWithState<T>(T state, TestHttpClient.Entry entry, string etag = null)
|
private async void SendResponseWithState<T>(T state, TestHttpClient.Entry entry, string etag = null)
|
||||||
{
|
{
|
||||||
var stateDate = ProtobufUtils.ConvertToByteStringAsync(state);
|
var stateDate = TypeConverters.ToJsonByteString(state);
|
||||||
var stateResponse = new Autogenerated.GetStateResponse();
|
var stateResponse = new Autogenerated.GetStateResponse();
|
||||||
stateResponse.Data = stateDate;
|
stateResponse.Data = stateDate;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Dapr.Client.Test
|
||||||
|
{
|
||||||
|
using Dapr.Client.Autogen.Test.Grpc.v1;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
public class TypeConvertersTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AnyConversion_GRPC_Pack_Unpack()
|
||||||
|
{
|
||||||
|
var testRun = new TestRun();
|
||||||
|
testRun.Tests.Add(new TestCase() { Name = "test1" });
|
||||||
|
testRun.Tests.Add(new TestCase() { Name = "test2" });
|
||||||
|
testRun.Tests.Add(new TestCase() { Name = "test3" });
|
||||||
|
|
||||||
|
var any = TypeConverters.ToAny(testRun);
|
||||||
|
var type = TypeConverters.FromAny<TestRun>(any);
|
||||||
|
|
||||||
|
type.Should().BeEquivalentTo(testRun);
|
||||||
|
any.TypeUrl.Should().Be("type.googleapis.com/TestRun");
|
||||||
|
type.Tests.Count.Should().Be(3);
|
||||||
|
type.Tests[0].Name.Should().Be("test1");
|
||||||
|
type.Tests[1].Name.Should().Be("test2");
|
||||||
|
type.Tests[2].Name.Should().Be("test3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AnyConversion_JSON_Serialization_Deserialization()
|
||||||
|
{
|
||||||
|
var response = new Response()
|
||||||
|
{
|
||||||
|
Name = "test"
|
||||||
|
};
|
||||||
|
|
||||||
|
var any = TypeConverters.ToAny(response);
|
||||||
|
var type = TypeConverters.FromAny<Response>(any);
|
||||||
|
|
||||||
|
type.Should().BeEquivalentTo(response);
|
||||||
|
any.TypeUrl.Should().Be("Dapr.Client.Test.TypeConvertersTest+Response");
|
||||||
|
type.Name.Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Response
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
// ------------------------------------------------------------
|
|
||||||
// Copyright (c) Microsoft Corporation.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
// ------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace Dapr
|
|
||||||
{
|
|
||||||
using System.Text.Json;
|
|
||||||
using Google.Protobuf;
|
|
||||||
using Google.Protobuf.WellKnownTypes;
|
|
||||||
|
|
||||||
public class ProtobufUtils
|
|
||||||
{
|
|
||||||
public static Any ConvertToAnyAsync<T>(T data, JsonSerializerOptions options = null)
|
|
||||||
{
|
|
||||||
var any = new Any();
|
|
||||||
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options);
|
|
||||||
any.Value = ByteString.CopyFrom(bytes);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return any;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ByteString ConvertToByteStringAsync<T>(T data, JsonSerializerOptions options = null)
|
|
||||||
{
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
var bytes = JsonSerializer.SerializeToUtf8Bytes(data, options);
|
|
||||||
return ByteString.CopyFrom(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
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